Performant Daten behandeln mit modernem C++

Wir werden häufig mit Missverständnissen und Vorurteilen bezüglich der Performance von C++ Programmen konfrontiert. In diesem Beitrag, der auf seinem Vortrag bei unserem Kundentag am 31.03.2017 beruht, entkräftet Marcus Powarzynski einige dieser Vorurteile.

Im Beratungs- und Schulungsgeschäft der letzten Jahre ist mir aufgefallen, dass sich bzgl. C++ einige Missverständnisse bzw. Vorurteile hartnäckig festgesetzt haben. Vor allem Entwickler, die ursprünglich aus der C-Programmierung kommen, äußern oft Bedenken bezüglich der Performance und Echtzeitfähigkeit von C++ Programmen. Es gibt übrigens auch eine lange Liste von unbegründeten Vorurteilen gegenüber C++, die aus der JAVA Community kommen und seit ca. 15 Jahren ohne Überprüfung ständig wiederholt werden. Aber das ist ein anderes Thema … ;-)

Liebe Fans der Programmiersprache C: Mit C++ kann man genauso effizient Daten behandeln!

Von den Embedded-Entwicklern höre ich häufig:

  1. Bei der Behandlung von Daten als Werte (Wertsemantik), insbesondere beim Weiterreichen von Funktion zu Funktion erstellt C++ unkontrollierbar und unnötig Kopien der Werte.
  2. Sobald man C++ Sprachkonstrukte nutzt, die es in C nicht gibt, kommt dynamische Speicherverwaltung ins Spiel. Man kann sich nicht gegen die Verwendung des Heaps wehren. Damit scheiden diese Sprachkonstrukte für fast alle Embedded-Projekte und vor allem für Echtzeit-Projekte aus.
  3. Es gibt zwar vorbereitete Datenstrukturen in der C++ Standardbibliothek, die sind aber alle speicherverschwendend, viel langsamer als handgeschriebene, die das selbe tun und für sie trifft Punkt 2 zu. Ich höre von verschiedenen Entwicklern fast unisono immer wieder: „Schreib’ dir deine verkettete Liste lieber selbst!“
Hallo liebe C-Kollegen! Ich verstehe sehr gut, dass solche Eindrücke entstehten. Aber ich muss Ihnen sagen: Alle genannten Behauptungen treffen nicht zu!

In diesem kurzen Blog-Eindruck möchte ich nur ein paar Dinge anreißen – wer sich für mehr interessiert, ist herzlich eingeladen, mit uns Kontakt aufzunehmen!

Zu Punkt 1: C++ wurde von der ersten Stunde an darauf ausgelegt, die Behandlung von Daten direkt als Werte und über Pointer möglichst effizient umsetzen zu können. Dies ist mit C++11 nochmals optimiert worden. Zugegeben: Es benötigt einige Einarbeitung, um die Beeinflussung des Lebenszyklus eines Objektes in C++ zu verstehen und optimal zu nutzen. Hat man aber mal Copy Constructor, Assignment Operator, Move Semantics und Perfect Forwarding verstanden, ist es ganz leicht wie in C Zero Copy umzusetzen – also das Übergeben von Werten ohne irgendeine Kopie! Und das ganz sauber, ohne Tricksereien mit Pointern und ohne „Aufweichung“ der Objektorientierung!

Zu Punkt 2: Ja, es stimmt, in der C++ Welt herrscht die Philosophie: Viele Dinge sind implizit vorgegeben, wenn man sie ändern möchte, muss man das tun und wissen, wie man es tut. Dies trifft auch auf die Benutzung des Heaps zu. Trotzdem ist es ein Leichtes, den Heap komplett „abzuklemmen“, auf Memory Pools umzustellen und trotzdem effizient die „schicken“ Smart Pointer zu nutzen. Deleters und Allocators sind die Stichworte. Effiziente Memory Pools gibt es in den weit verbreiteten, also quasi standardisierten Bibliotheken einige. Auch welche, die performant in einer Multi-Threading-Architektur eingesetzt werden können (natürlich lock-free!).

Damit kommt man in den Genuss einer definierbaren WCET (Worst Case Execution Time) und muss keine Angst mehr vor Heap Fragmentation haben – beides wichtige Vorrausetzungen für echtzeitfähigen Code.

Und wie oben gesagt: Alles sauber gekapselt, keine Kompromisse in Sachen Objektorientierung.

Zu Punkt 3: Ja, die erste Version der C++ Standard-Bibliothek bot nicht viele Datenstrukturen (Container-Klassen), man hat z.B. eine performante einfach-verkettete Liste vermisst, Hashing gabs überhaupt nicht. Doch diese Zeiten sind längst vorbei.

Und wenn die Auswahl, die die Standardbibliothek von ISO-C++ bietet, immer noch nicht befriedigt – im Quasi-Standard gibt es eine Fülle von Container-Klassen, die einen optimalen Trade Off zwischen komfortabler Schnittstelle und Performanz berücksichtigen.

Dass man durch die Benutzung dieser Container-Klassen auch die Möglichkeit hat, die vielen performanten Funktionen zu nutzen, wie Suchen und Sortieren, dürfte klar sein.

Ja, aber … nutzen denn dynamische, verkettete Datenstrukturen bei der Element-Erzeugung nicht den Heap? Ich glaube, dazu muss ich nichts mehr schreiben, sondern einfach auf Punkt 2 verweisen.

Viele Grüße an die C-Fans (bin selbst einer, aber noch viel mehr von C++ überzeugt)

Telefon: 07247-954550
info@maurer-treutner.de