Ja, gut geschriebenes C ++ ist erheblich schneller. Wenn Sie leistungskritische Programme schreiben und Ihr C ++ nicht so schnell wie C ist (oder innerhalb weniger Prozent), stimmt etwas nicht. Wenn Ihre ObjC-Implementierung so schnell wie C ist, stimmt normalerweise etwas nicht - dh das Programm ist wahrscheinlich ein schlechtes Beispiel für ObjC OOD, da es wahrscheinlich einige "schmutzige" Tricks verwendet, um unter die Abstraktionsschicht zu treten, in der es arbeitet, z. B. direkt ivar zugreift.
Der 'Vergleich' von Mike Ash ist sehr irreführend - ich würde niemals empfehlen, die Ausführungszeiten von Programmen zu vergleichen, die Sie geschrieben haben, oder C vs C ++ vs ObjC zu vergleichen. Die dargestellten Ergebnisse stammen aus einem Test mit deaktivierten Compiler-Optimierungen . Ein Programm, das mit deaktivierten Optimierungen kompiliert wurde, ist selten relevant, wenn Sie Ausführungszeiten messen. Es ist fehlerhaft, es als Benchmark anzusehen, der C ++ mit Objective-C vergleicht. Der Test vergleicht auch einzelne Funktionen und nicht ganze, für die reale Welt optimierte Implementierungen. Einzelne Funktionen werden mit beiden Sprachen auf sehr unterschiedliche Weise kombiniert. Dies ist alles andere als ein realistischer Leistungsmaßstab für optimierte Implementierungen. Beispiele: Mit aktivierten Optimierungen ,IMP
Der Cache ist so langsam wie virtuelle Funktionsaufrufe. Der statische Versand (im Gegensatz zum dynamischen Versand, z. B. unter Verwendung virtual
) und Aufrufe bekannter C ++ - Typen (bei denen der dynamische Versand möglicherweise umgangen wird) können aggressiv optimiert werden. Dieser Prozess wird als Devirtualisierung bezeichnet, und wenn er verwendet wird, kann eine deklarierte Elementfunktion virtual
sogar inline
d sein. Im Fall des Mike Ash-Tests, bei dem viele Aufrufe an deklarierte Mitgliedsfunktionen virtual
mit leeren Körpern erfolgen: Diese Aufrufe werden in optimierten Builds vollständig wenn der Typ bekannt ist, da der Compiler die Implementierung sieht und den dynamischen Versand bestimmen kann nicht notwendig. Der Compiler kann auch Aufrufe von eliminierenmalloc
optimiert (zugunsten der Stapelspeicherung). Das Aktivieren von Compiler-Optimierungen in C, C ++ oder Objective-C kann daher zu dramatischen Unterschieden bei den Ausführungszeiten führen.
Das heißt nicht, dass die präsentierten Ergebnisse völlig nutzlos sind. Sie können nützliche Informationen zu externen APIs erhalten, wenn Sie feststellen möchten, ob es messbare Unterschiede zwischen der Zeit gibt, die sie auf pthread_create
oder +[NSObject alloc]
auf einer Plattform oder Architektur verbringen, und einer anderen. In diesen beiden Beispielen werden natürlich optimierte Implementierungen in Ihrem Test verwendet (es sei denn, Sie entwickeln sie gerade). Aber um eine Sprache in Programmen, die Sie kompilieren, mit einer anderen zu vergleichen, sind die dargestellten Ergebnisse bei deaktivierten Optimierungen nutzlos.
Objekterstellung
Berücksichtigen Sie auch die Objekterstellung in ObjC - jedes Objekt wird dynamisch zugewiesen (z. B. auf dem Heap). Mit C ++ können Objekte auf dem Stapel (z. B. ungefähr so schnell wie das Erstellen einer C-Struktur und das Aufrufen einer einfachen Funktion in vielen Fällen), auf dem Heap oder als Elemente abstrakter Datentypen erstellt werden. Jedes Mal, wenn Sie zuweisen und freigeben (z. B. über malloc / free), können Sie eine Sperre einführen. Wenn Sie eine C-Struktur oder ein C ++ - Objekt auf dem Stapel erstellen, ist keine Sperre erforderlich (obwohl interne Mitglieder möglicherweise Heap-Zuweisungen verwenden), und dies kostet häufig nur einige Anweisungen oder einige Anweisungen plus einen Funktionsaufruf.
ObjC-Objekte sind ebenfalls Instanzen mit Referenzzählung. Die tatsächliche Notwendigkeit, dass ein Objekt ein std::shared_ptr
leistungskritisches C ++ ist, ist sehr selten. In C ++ ist es nicht erforderlich oder wünschenswert, jede Instanz zu einer gemeinsam genutzten Instanz mit Referenzzählung zu machen. Mit C ++ haben Sie viel mehr Kontrolle über Besitz und Lebensdauer.
Arrays und Sammlungen
Arrays und viele Sammlungen in C und C ++ verwenden ebenfalls stark typisierte Container und zusammenhängenden Speicher. Da die Adresse der Mitglieder des nächsten Elements häufig bekannt ist, kann der Optimierer viel mehr und Sie haben eine hervorragende Cache- und Speicherlokalität. Mit ObjC ist dies für Standardobjekte (z NSObject
. B. ) weit von der Realität entfernt .
Versand
In Bezug auf Methoden verwenden viele C ++ - Implementierungen nur wenige virtuelle / dynamische Aufrufe, insbesondere in hochoptimierten Programmen. Dies sind statische Methodenaufrufe und Futter für die Optimierer.
Bei ObjC-Methoden ist jeder Methodenaufruf (objc message send) dynamisch und folglich eine Firewall für den Optimierer. Letztendlich führt dies zu vielen Einschränkungen oder Unannehmlichkeiten in Bezug darauf, was Sie tun können und was nicht, um die Leistung beim Schreiben von leistungskritischem ObjC auf ein Minimum zu beschränken. Dies kann zu größeren Methoden, IMP-Caching und häufiger Verwendung von C führen.
Einige Echtzeitanwendungen können keine ObjC-Nachrichten in ihren Renderpfaden verwenden. Keine - Audio-Rendering ist ein gutes Beispiel dafür. Der ObjC-Versand ist einfach nicht für Echtzeitzwecke konzipiert. Zuweisungen und Sperren können hinter den Kulissen beim Versenden von Nachrichtenobjekten auftreten, wodurch die Komplexität / Zeit des Objektnachrichtens unvorhersehbar genug wird, dass das Audio-Rendering möglicherweise seine Frist verpasst.
Andere Eigenschaften
C ++ bietet auch Generika- / Vorlagenimplementierungen für viele seiner Bibliotheken. Diese optimieren sehr gut. Sie sind typsicher, und viele Inlining- und Optimierungsvorgänge können mit Vorlagen durchgeführt werden (Polymorphismus, Optimierung und Spezialisierung, die bei der Kompilierung stattfinden). C ++ fügt mehrere Funktionen hinzu, die in striktem ObjC einfach nicht verfügbar oder vergleichbar sind. Der Versuch, langs, Objekte und Bibliotheken, die sehr unterschiedlich sind, direkt zu vergleichen, ist nicht so nützlich - es ist eine sehr kleine Teilmenge der tatsächlichen Realisierungen. Es ist besser, die Frage auf eine Bibliothek / ein Framework oder ein reales Programm zu erweitern, wobei viele Aspekte des Designs und der Implementierung berücksichtigt werden.
Andere Punkte
C- und C ++ - Symbole können in verschiedenen Phasen des Builds (Strippen, Eliminieren von totem Code, Inlining und frühes Inlining sowie Optimierung der Verbindungszeit) einfacher entfernt und optimiert werden. Zu den Vorteilen gehören reduzierte Binärgrößen, reduzierte Start- / Ladezeiten, reduzierter Speicherverbrauch usw. Für eine einzelne App ist dies möglicherweise keine so große Sache. Wenn Sie jedoch viel Code wiederverwenden und dies auch tun sollten, könnten Ihre gemeinsam genutzten Bibliotheken dem Programm bei Verwendung von ObjC viel unnötiges Gewicht hinzufügen - es sei denn, Sie sind bereit, durch einige brennende Reifen zu springen. Skalierbarkeit und Wiederverwendung sind daher auch Faktoren bei mittleren / großen Projekten und Gruppen, bei denen die Wiederverwendung hoch ist.
Enthaltene Bibliotheken
ObjC-Bibliotheksimplementierer optimieren auch für die Umgebung, sodass die Bibliotheksimplementierer einige Sprach- und Umgebungsfunktionen verwenden können, um optimierte Implementierungen anzubieten. Obwohl es beim Schreiben eines optimierten Programms in reinem ObjC einige ziemlich erhebliche Einschränkungen gibt, gibt es in Cocoa einige hochoptimierte Implementierungen. Dies ist eine der Stärken von Cocoa, obwohl die C ++ - Standardbibliothek (wie manche Leute die STL nennen) auch keine Lücke ist. Cocoa arbeitet auf einer viel höheren Abstraktionsebene als C ++ - wenn Sie nicht genau wissen, was Sie tun (oder tun sollten), kann es Sie wirklich kosten , näher am Metall zu arbeiten. Es ist eine gute Sache, auf eine gute Bibliotheksimplementierung zurückzugreifen, wenn Sie kein Experte auf einem bestimmten Gebiet sind, es sei denn, Sie sind wirklich bereit zu lernen. Auch die Umgebungen von Cocoa sind begrenzt. Sie können Implementierungen / Optimierungen finden, die das Betriebssystem besser nutzen.
Wenn Sie optimierte Programme schreiben und Erfahrung in C ++ und ObjC haben, sind saubere C ++ - Implementierungen oft doppelt so schnell oder schneller als sauberes ObjC (ja, Sie können es mit Cocoa vergleichen). Wenn Sie wissen, wie man optimiert, können Sie häufig bessere Abstraktionen als allgemeine Abstraktionen auf höherer Ebene durchführen. Einige optimierte C ++ - Implementierungen sind jedoch genauso schnell oder langsamer als die von Cocoa (z. B. war mein erster Versuch, Datei-E / A zu erstellen, langsamer als der von Cocoa - hauptsächlich, weil die C ++ - Implementierung den Speicher initialisiert).
Vieles hängt von den Sprachfunktionen ab, mit denen Sie vertraut sind. Ich benutze beide langs, beide haben unterschiedliche Stärken und Modelle / Muster. Sie ergänzen sich recht gut und es gibt großartige Bibliotheken für beide. Wenn Sie ein komplexes, leistungskritisches Programm implementieren, bietet die korrekte Verwendung der Funktionen und Bibliotheken von C ++ viel mehr Kontrolle und bietet erhebliche Vorteile für die Optimierung, sodass in den richtigen Händen "um ein Vielfaches schneller" eine gute Standarderwartung ist ( Erwarten Sie jedoch nicht, jedes Mal oder ohne Arbeit zu gewinnen. Denken Sie daran, es dauert Jahre, um C ++ gut genug zu verstehen, um diesen Punkt wirklich zu erreichen.
Ich behalte den Großteil meiner leistungskritischen Pfade als C ++ bei, erkenne aber auch, dass ObjC auch eine sehr gute Lösung für einige Probleme ist und dass einige sehr gute Bibliotheken verfügbar sind.