Ich war schon immer ein Embedded-Software-Ingenieur, aber normalerweise auf Schicht 3 oder 2 des OSI-Stacks. Ich bin nicht wirklich ein Hardware-Typ. Ich habe im Allgemeinen immer Telekommunikationsprodukte hergestellt, normalerweise Hand- / Mobiltelefone, was im Allgemeinen so etwas wie einen ARM 7-Prozessor bedeutet.
Jetzt befinde ich mich in einer allgemeineren eingebetteten Welt, in einem kleinen Start-up, in dem ich möglicherweise zu "nicht so leistungsstarken" Prozessoren übergehe (es gibt das subjektive Bit) - ich kann nicht vorhersagen, welche.
Ich habe viel über die Debatte über die Verwendung von STL in C ++ in eingebetteten Systemen gelesen und es gibt keine eindeutige Antwort. Es gibt einige kleine Bedenken hinsichtlich der Portabilität und einige hinsichtlich der Codegröße oder der Laufzeit, aber ich habe zwei Hauptprobleme:
1 - Ausnahmebehandlung; Ich bin mir immer noch nicht sicher, ob ich es verwenden soll (siehe Embedded C ++: Ausnahmen verwenden oder nicht? )
2 - Ich mag die dynamische Speicherzuweisung in eingebetteten Systemen aufgrund der damit verbundenen Probleme nicht. Ich habe im Allgemeinen einen Pufferpool, der zur Kompilierungszeit statisch zugewiesen wird und der nur Puffer mit fester Größe bereitstellt (wenn keine Puffer vorhanden sind, System zurücksetzen). Die STL führt natürlich eine Menge dynamischer Zuweisungen durch.
Jetzt muss ich die Entscheidung treffen, ob ich die STL verwenden oder darauf verzichten möchte - für das gesamte Unternehmen für immer (es geht um einige Kern-S / W).
In welche Richtung springe ich? Super sicher und viel von dem verlieren, was C ++ ausmacht (imo, es ist mehr als nur die Sprachdefinition) und vielleicht später auf Probleme stoßen oder jetzt viel Ausnahmebehandlung und vielleicht etwas anderen Code hinzufügen müssen?
Ich bin versucht, einfach mit Boost zu arbeiten , aber 1) ich bin nicht sicher, ob es auf jeden eingebetteten Prozessor portiert, den ich verwenden möchte, und 2) auf ihrer Website sagen sie, dass sie bestimmte Teile davon nicht garantieren / empfehlen für eingebettete Systeme (insbesondere FSMs, was seltsam erscheint). Wenn ich mich für Boost entscheide und wir später ein Problem finden ...
Antworten:
Wir haben eine ähnliche Debatte in der Spielwelt und die Leute kommen auf beiden Seiten runter. Was den zitierten Teil betrifft, warum sollten Sie sich Sorgen machen, "viel von dem zu verlieren, was C ++ ausmacht"? Wenn es nicht pragmatisch ist, verwenden Sie es nicht. Es sollte keine Rolle spielen, ob es "C ++" ist oder nicht.
Führen Sie einige Tests durch. Können Sie die Speicherverwaltung von STL auf eine Weise umgehen, die Sie zufriedenstellt? Wenn ja, hat sich die Mühe gelohnt? Viele Probleme, die STL und Boost lösen sollen, treten einfach nicht auf, wenn Sie eine zufällige dynamische Speicherzuweisung vermeiden möchten. Löst STL ein bestimmtes Problem, mit dem Sie konfrontiert sind?
Viele Menschen haben STL in engen Umgebungen angegangen und waren damit zufrieden. Viele Leute meiden es einfach. Einige Leute schlagen völlig neue Standards vor . Ich glaube nicht, dass es eine richtige Antwort gibt.
quelle
Ich arbeite jeden Tag an eingebetteten Echtzeitsystemen. Natürlich kann meine Definition des eingebetteten Systems anders sein als Ihre. Wir nutzen jedoch die STL und Ausnahmen in vollem Umfang und haben keine unüberschaubaren Probleme. Wir verwenden auch dynamischen Speicher (mit einer sehr hohen Rate; Zuweisung vieler Pakete pro Sekunde usw.) und mussten noch nicht auf benutzerdefinierte Zuweiser oder Speicherpools zurückgreifen. Wir haben sogar C ++ in Interrupt-Handlern verwendet. Wir verwenden keinen Boost, sondern nur, weil eine bestimmte Regierungsbehörde uns nicht zulässt.
Nach unserer Erfahrung können Sie in der Tat viele moderne C ++ - Funktionen in einer eingebetteten Umgebung verwenden, solange Sie Ihren Kopf verwenden und Ihre eigenen Benchmarks durchführen. Ich empfehle Ihnen dringend, Scott Meyers Effective C ++ 3rd Edition sowie die C ++ - Codierungsstandards von Sutter und Alexandrescu zu verwenden, um Sie bei der Verwendung von C ++ mit einem vernünftigen Programmierstil zu unterstützen.
Bearbeiten: Nachdem ich 2 Jahre später eine positive Bewertung dazu erhalten habe, möchte ich ein Update veröffentlichen. Wir sind in unserer Entwicklung viel weiter fortgeschritten und haben endlich Stellen in unserem Code erreicht, an denen die Standardbibliothekscontainer unter Hochleistungsbedingungen zu langsam sind. Hier haben wir tatsächlich auf benutzerdefinierte Algorithmen, Speicherpools und vereinfachte Container zurückgegriffen. Das ist das Schöne an C ++: Sie können die Standardbibliothek verwenden und all die guten Dinge erhalten, die sie für 90% Ihrer Anwendungsfälle bietet. Sie werfen nicht alles weg, wenn Sie auf Probleme stoßen, sondern optimieren einfach die Problemstellen von Hand.
quelle
Die anderen Beiträge haben sich mit den wichtigen Themen der dynamischen Speicherzuweisung, Ausnahmen und möglichem Aufblähen des Codes befasst. Ich möchte nur hinzufügen: Vergiss nicht
<algorithm>
! Unabhängig davon , ob Sie STL - Vektoren oder Ebene C - Arrays und Zeiger, können Sie verwendensort()
,binary_search()
,random_shuffle()
die Funktionen für den Aufbau und die Verwaltung von Halden usw. Diese Routinen werden mit ziemlicher Sicherheit schneller und weniger Buggy als Versionen zum Selberbauen.Beispiel: Wenn Sie nicht sorgfältig darüber nachdenken, führt ein von Ihnen selbst erstellter Shuffle-Algorithmus wahrscheinlich zu verzerrten Verteilungen .
random_shuffle()
Gewohnheit.quelle
Paul Pedriana von Electronic Arts schrieb 2007 eine lange Abhandlung darüber, warum die STL für die Entwicklung eingebetteter Konsolen ungeeignet war und warum sie ihre eigenen schreiben mussten. Es ist ein ausführlicher Artikel, aber die wichtigsten Gründe waren:
Vor einigen Jahren hat unser Unternehmen beschlossen, die STL überhaupt nicht zu verwenden, sondern stattdessen ein eigenes Containersystem zu implementieren, das maximal leistungsfähig, einfacher zu debuggen und speicherkonservativer ist. Es war viel Arbeit, aber es hat sich um ein Vielfaches zurückgezahlt. In unserem Bereich konkurrieren Produkte jedoch darum, wie viel sie mit einer bestimmten CPU- und Speichergröße auf 16,6 ms erreichen können.
Ausnahmen: Sie sind auf Konsolen langsam , und jeder, der Ihnen etwas anderes sagt, hat nicht versucht, sie zeitlich zu steuern. Durch einfaches Kompilieren mit aktivierten Funktionen wird das gesamte Programm aufgrund des erforderlichen Prolog- / Epilog-Codes verlangsamt. Messen Sie es selbst, wenn Sie mir nicht glauben. Bei geordneten CPUs ist es sogar noch schlimmer als beim x86. Aus diesem Grund unterstützt der von uns verwendete Compiler nicht einmal C ++ - Ausnahmen.
Der Leistungsgewinn besteht nicht so sehr darin, die Kosten eines Ausnahmewurfs zu vermeiden, sondern darin, Ausnahmen vollständig zu deaktivieren.
quelle
/EHa
und dann mit/EHsc
/ Fa zu kompilieren (nicht zu verknüpfen) , um eine Baugruppenliste zu erstellen. In beiden Fällen wird die SEH-Verwaltung (Structured Exception Handling) von Win32 eingeführt. Dies ist das zusätzliche Verschieben von Daten auf den Stapel und das Einstellen desFS
Segmentregisters.Lassen Sie mich zunächst sagen, dass ich seit einigen Jahren keine eingebettete Arbeit mehr geleistet habe und niemals in C ++. Mein Rat ist also jeden Cent wert, den Sie dafür bezahlen ...
Die von STL verwendeten Vorlagen generieren niemals Code, den Sie nicht selbst generieren müssten, sodass ich mir keine Sorgen über das Aufblähen von Code machen würde.
Die STL löst keine eigenen Ausnahmen aus, daher sollte dies kein Problem darstellen. Wenn Ihre Klassen nicht werfen, sollten Sie in Sicherheit sein. Teilen Sie Ihre Objektinitialisierung in zwei Teile, lassen Sie den Konstruktor ein Bare-Bones-Objekt erstellen und führen Sie dann eine Initialisierung durch, die in einer Elementfunktion fehlschlagen könnte, die einen Fehlercode zurückgibt.
Ich denke, mit allen Containerklassen können Sie Ihre eigene Zuordnungsfunktion definieren. Wenn Sie also aus einem Pool zuordnen möchten, können Sie dies ermöglichen.
quelle
vector::at()
ist ein gutes Beispiel. Es wäre genauer zu sagen, dass die STL so verwendet werden kann, dass sie niemals Ausnahmen erzeugt (hier durch Verwendungoperator[]()
anstelle vonat()
) und ohne zusätzliche Kompromisse einzugehen.vector::at
und wahrscheinlich auch ein paar andere - danke für die Klarstellung. Es sollte möglich sein, Ihre Standardbibliotheksdateien nach "throw" zu durchsuchen und alle "Ausnahmen" zu meiner übermäßig verallgemeinerten Aussage zu finden.Das Open-Source-Projekt "Embedded Template Library (ETL)" zielt auf die üblichen Probleme mit der in eingebetteten Anwendungen verwendeten STL ab, indem eine Bibliothek bereitgestellt / implementiert wird:
Sie können auch eine kommerzielle C ++ - STL für eingebettete Entwickler in Betracht ziehen, die von ESR Labs bereitgestellt wird.
quelle
Für die Speicherverwaltung können Sie einen eigenen Allokator implementieren, der Speicher aus dem Pool anfordert. Und alle STL-Container haben eine Vorlage für den Allokator.
Ausnahmsweise löst STL nicht viele Ausnahmen aus. Im Allgemeinen sind die häufigsten: In Ihrem Fall sollte das System zurückgesetzt werden, damit Sie im Allokator zurücksetzen können. andere sind beispielsweise außerhalb der Reichweite, Sie können es vom Benutzer vermeiden.
Ich denke, Sie können STL im eingebetteten System verwenden :)
quelle
Zusätzlich zu allen Kommentaren würde ich vorschlagen, dass Sie den technischen Bericht zur C ++ - Leistung lesen, der sich speziell mit Themen befasst, an denen Sie interessiert sind: Verwendung von C ++ in Embedded (einschließlich harter Echtzeitsysteme); wie die Ausnahmebehandlung normalerweise implementiert wird und welchen Overhead sie hat; Overhead der kostenlosen Filialzuweisung.
Der Bericht ist wirklich gut, ebenso wie viele beliebte Details zur C ++ - Leistung.
quelle
Dies hängt im Wesentlichen von Ihrem Compiler und der Größe Ihres Arbeitsspeichers ab. Wenn Sie mehr als ein paar KB RAM haben, hilft eine dynamische Speicherzuweisung sehr. Wenn die Implementierung von malloc aus der Standardbibliothek, die Sie haben, nicht auf Ihre Speichergröße abgestimmt ist, können Sie Ihre eigene schreiben, oder es gibt nette Beispiele wie mm_malloc von Ralph Hempel , mit denen Sie Ihre neuen Operatoren und Löschoperatoren darüber schreiben können .
Ich stimme nicht mit denen überein, die das Mem wiederholen, dass Ausnahmen und stl-Container zu langsam oder zu aufgebläht sind usw. Natürlich fügt es etwas mehr Code hinzu als das Malloc eines einfachen C, aber die vernünftige Verwendung von Ausnahmen kann den Code viel klarer und klarer machen Vermeiden Sie zu viele Fehler beim Überprüfen des Klappentextes in C.
Man muss bedenken, dass STL-Allokatoren ihre Allokationen in Zweierpotenzen erhöhen, was bedeutet, dass sie manchmal einige Neuzuweisungen vornehmen, bis sie die richtige Größe erreicht haben, die Sie mit Zurückhaltung verhindern können, damit sie so billig wie ein Malloc des gewünschten wird Größe, wenn Sie die Größe kennen, die Sie trotzdem zuweisen möchten.
Wenn Sie beispielsweise einen großen Puffer in einem Vektor haben, wird möglicherweise irgendwann eine Neuzuweisung durchgeführt und das 1,5-fache der Speichergröße verbraucht, die Sie zu einem bestimmten Zeitpunkt beim Neuzuweisen und Verschieben von Daten verwenden möchten. (Zum Beispiel werden irgendwann N Bytes zugewiesen, Sie fügen Daten über Anhängen oder einen Einfügeiterator hinzu und es werden 2N Bytes zugewiesen, die ersten N kopiert und N freigegeben. Irgendwann sind 3N Bytes zugewiesen.)
Am Ende hat es also viele Vorteile und zahlt sich aus, wenn Sie wissen, was Sie tun. Sie sollten ein wenig darüber wissen, wie C ++ funktioniert, um es ohne Überraschungen in eingebetteten Projekten zu verwenden.
Und für den Mann mit den festen Puffern und dem Zurücksetzen können Sie jederzeit innerhalb des neuen Operators oder was auch immer zurücksetzen, wenn Sie nicht genügend Speicher haben. Dies würde jedoch bedeuten, dass Sie ein schlechtes Design erstellt haben, das Ihren Speicher erschöpfen kann.
Eine Ausnahme, die mit ARM Realview 3.1 ausgelöst wird:
--- OSD\#1504 throw fapi_error("OSDHANDLER_BitBlitFill",res); S:218E72F0 E1A00000 MOV r0,r0 S:218E72F4 E58D0004 STR r0,[sp,#4] S:218E72F8 E1A02000 MOV r2,r0 S:218E72FC E24F109C ADR r1,{pc}-0x94 ; 0x218e7268 S:218E7300 E28D0010 ADD r0,sp,#0x10 S:218E7304 FA0621E3 BLX _ZNSsC1EPKcRKSaIcE <0x21a6fa98> S:218E7308 E1A0B000 MOV r11,r0 S:218E730C E1A0200A MOV r2,r10 S:218E7310 E1A01000 MOV r1,r0 S:218E7314 E28D0014 ADD r0,sp,#0x14 S:218E7318 EB05C35F BL fapi_error::fapi_error <0x21a5809c> S:218E731C E3A00008 MOV r0,#8 S:218E7320 FA056C58 BLX __cxa_allocate_exception <0x21a42488> S:218E7324 E58D0008 STR r0,[sp,#8] S:218E7328 E28D1014 ADD r1,sp,#0x14 S:218E732C EB05C340 BL _ZN10fapi_errorC1ERKS_ <0x21a58034> S:218E7330 E58D0008 STR r0,[sp,#8] S:218E7334 E28D0014 ADD r0,sp,#0x14 S:218E7338 EB05C36E BL _ZN10fapi_errorD1Ev <0x21a580f8> S:218E733C E51F2F98 LDR r2,0x218e63ac <OSD\#1126> S:218E7340 E51F1F98 LDR r1,0x218e63b0 <OSD\#1126> S:218E7344 E59D0008 LDR r0,[sp,#8] S:218E7348 FB056D05 BLX __cxa_throw <0x21a42766>
Scheint nicht so beängstigend und es wird kein Overhead in {} Blöcken oder Funktionen hinzugefügt, wenn die Ausnahme nicht ausgelöst wird.
quelle
Das größte Problem mit STL in eingebetteten Systemen ist das Problem der Speicherzuweisung (das, wie Sie sagten, viele Probleme verursacht).
Ich würde ernsthaft nachforschen, um eine eigene Speicherverwaltung zu erstellen, die durch Überschreiben der Operatoren new / delete erstellt wird. Ich bin mir ziemlich sicher, dass es mit ein bisschen Zeit geschafft werden kann, und es lohnt sich mit ziemlicher Sicherheit.
Was die Ausnahme betrifft, würde ich nicht dorthin gehen. Ausnahmen stellen eine ernsthafte Verlangsamung Ihres Codes dar, da sie dazu führen, dass jeder einzelne Block (
{ }
) vorher und nachher Code enthält, wodurch die Ausnahme abgefangen und alle darin enthaltenen Objekte zerstört werden können. Ich habe keine genauen Daten dazu zur Hand, aber jedes Mal, wenn ich dieses Problem gesehen habe, habe ich überwältigende Beweise für eine massive Verlangsamung gesehen, die durch die Verwendung von Ausnahmen verursacht wurde.Bearbeiten:
Da viele Leute Kommentare geschrieben haben, die besagen, dass die Ausnahmebehandlung nicht langsamer ist, dachte ich, ich würde diese kleine Notiz hinzufügen (danke für die Leute, die dies in Kommentaren geschrieben haben, ich dachte, es wäre gut, sie hier hinzuzufügen).
Der Grund, warum die Ausnahmebehandlung Ihren Code verlangsamt, besteht darin, dass der Compiler sicherstellen muss, dass jeder Block (
{}
) von der Stelle, an der eine Ausnahme ausgelöst wird, bis zu der Stelle, an der er behandelt wird, alle darin enthaltenen Objekte freigeben muss. Dies ist Code, der jedem Block hinzugefügt wird, unabhängig davon, ob jemals jemand eine Ausnahme auslöst oder nicht (da der Compiler zur Kompilierungszeit nicht feststellen kann, ob dieser Block Teil einer Ausnahmekette ist).Natürlich könnte dies eine alte Methode sein, um Dinge zu tun, die in neueren Compilern viel schneller geworden sind (ich bin nicht gerade auf dem neuesten Stand der C ++ - Compileroptimierungen). Der beste Weg, dies zu wissen, besteht darin, nur einen Beispielcode mit aktivierten und deaktivierten Ausnahmen (der einige verschachtelte Funktionen enthält) auszuführen und den Unterschied zeitlich zu bestimmen.
quelle
try
Block einenEXCEPTION_REGISTRATION
Block auf dem Stapel einrichten und das FS-Register darauf richten . Dies geschieht unabhängig davon, ob tatsächlich Ausnahmen auftreten. Quelle: microsoft.com/msj/0197/exception/exception.aspx Außerdem muss der Compiler jedem Block Code hinzufügen, der Objekte mit nicht trivialen Destruktoren deklariert, es sei denn, er kann nachweisen, dass innerhalb des Blocks keine Ausnahme auftreten kann. Wie werden diese Objekte sonst beim Abwickeln des Stapels zerstört?In unserem Embedded-Scanner-Projekt haben wir ein Board mit ARM7-CPU entwickelt, und STL brachte keine Probleme mit sich. Sicherlich sind die Projektdetails wichtig, da die dynamische Speicherzuweisung für viele heute verfügbare Boards und Projekttypen möglicherweise kein Problem darstellt.
quelle