JIT vs. Static Compiler
Wie bereits in den vorherigen Beiträgen erwähnt, kann JIT IL / Bytecode zur Laufzeit in nativen Code kompilieren. Die Kosten dafür wurden erwähnt, aber nicht zu dem Schluss:
JIT hat ein großes Problem: Es kann nicht alles kompilieren: Das Kompilieren von JIT braucht Zeit, daher kompiliert das JIT nur einige Teile des Codes, während ein statischer Compiler eine vollständige native Binärdatei erzeugt: Für einige Programme die statische Der Compiler übertrifft die JIT einfach leicht.
Natürlich ist C # (oder Java oder VB) normalerweise schneller, um eine tragfähige und robuste Lösung zu erstellen als C ++ (schon allein deshalb, weil C ++ eine komplexe Semantik aufweist und die C ++ - Standardbibliothek zwar interessant und leistungsfähig ist, aber im Vergleich zur vollständigen Bibliothek ziemlich schlecht Umfang der Standardbibliothek von .NET oder Java), daher ist der Unterschied zwischen C ++ und .NET oder Java JIT für die meisten Benutzer normalerweise nicht sichtbar, und für die kritischen Binärdateien können Sie weiterhin die C ++ - Verarbeitung aufrufen von C # oder Java (auch wenn diese Art von nativen Aufrufen an sich ziemlich kostspielig sein kann) ...
C ++ - Metaprogrammierung
Beachten Sie, dass Sie normalerweise C ++ - Laufzeitcode mit dem entsprechenden Code in C # oder Java vergleichen. C ++ verfügt jedoch über eine Funktion, die Java / C # sofort übertreffen kann, nämlich das Metaprogrammieren von Vorlagen: Die Codeverarbeitung erfolgt zur Kompilierungszeit (wodurch die Kompilierungszeit erheblich verlängert wird), was zu einer Laufzeit von null (oder fast null) führt.
Ich habe noch keinen realen Effekt darauf gesehen (ich habe nur mit Konzepten gespielt, aber bis dahin war der Unterschied Sekunden der Ausführung für JIT und Null für C ++), aber dies ist erwähnenswert, neben der Tatsache, dass Metaprogrammierung nicht der Fall ist trivial...
Edit 2011-06-10: In C ++ wird das Spielen mit Typen zur Kompilierungszeit ausgeführt, dh es wird generischer Code erstellt, der nicht generischen Code aufruft (z. B. ein generischer Parser von Zeichenfolge zu Typ T, der die Standardbibliotheks-API für die erkannten Typen T aufruft). und den Parser für seinen Benutzer leicht erweiterbar zu machen) ist sehr einfach und sehr effizient, während das Äquivalent in Java oder C # bestenfalls schmerzhaft zu schreiben ist und zur Laufzeit immer langsamer und aufgelöst wird, selbst wenn die Typen zur Kompilierungszeit bekannt sind. Das heißt, Ihre einzige Hoffnung ist, dass die GEG das Ganze inline macht.
...
Edit 2011-09-20: Das Team hinter Blitz ++ ( Homepage , Wikipedia ) ist diesen Weg gegangen, und anscheinend ist es ihr Ziel, die Leistung von FORTRAN bei wissenschaftlichen Berechnungen zu erreichen, indem es so weit wie möglich von der Laufzeitausführung zur Kompilierungszeit über die Metaprogrammierung von C ++ - Vorlagen übergeht . Der Teil " Ich habe noch so einen realen Effekt auf diesen " gesehen, den ich oben geschrieben habe, existiert anscheinend im realen Leben.
Native C ++ - Speichernutzung
C ++ hat eine andere Speichernutzung als Java / C # und daher unterschiedliche Vorteile / Mängel.
Unabhängig von der JIT-Optimierung ist nichts so schnell wie der direkte Zeigerzugriff auf den Speicher (ignorieren wir für einen Moment Prozessor-Caches usw.). Wenn Sie also zusammenhängende Daten im Speicher haben, geht der Zugriff auf diese über C ++ - Zeiger (dh C-Zeiger ... Geben wir Caesar die Schuld) schneller als in Java / C #. Und C ++ hat RAII, was die Verarbeitung erheblich vereinfacht als in C # oder sogar in Java. C ++ muss using
die Existenz seiner Objekte nicht erfassen . Und C ++ hat keine finally
Klausel. Dies ist kein Fehler.
:-)
Und trotz C # -primitiver Strukturen kosten C ++ "on the stack" -Objekte bei Zuweisung und Zerstörung nichts und benötigen keinen GC, um in einem unabhängigen Thread für die Bereinigung zu arbeiten.
Was die Speicherfragmentierung betrifft, so sind Speicherzuordnungen im Jahr 2008 nicht die alten Speicherzuordnungen aus dem Jahr 1980, die normalerweise mit einer GC: C ++ - Zuweisung verglichen werden. Sie können zwar nicht in den Speicher verschoben werden, aber wie bei einem Linux-Dateisystem: Wer benötigt eine Festplatte? Defragmentierung, wenn keine Fragmentierung stattfindet? Die Verwendung des richtigen Allokators für die richtige Aufgabe sollte Teil des C ++ - Entwickler-Toolkits sein. Das Schreiben von Allokatoren ist nicht einfach, und dann haben die meisten von uns bessere Dinge zu tun, und für die meisten Anwendungen ist RAII oder GC mehr als gut genug.
Edit 2011-10-04: Beispiele für effiziente Allokatoren: Auf Windows-Plattformen ist seit Vista der Heap mit geringer Fragmentierung standardmäßig aktiviert. Für frühere Versionen kann der LFH durch Aufrufen der WinAPI-Funktion HeapSetInformation aktiviert werden . Auf anderen Betriebssystemen werden alternative Allokatoren bereitgestellt (siehehttps://secure.wikimedia.org/wikipedia/en/wiki/Malloc für eine Liste)
Jetzt wird das Speichermodell mit dem Aufkommen der Multicore- und Multithreading-Technologie etwas komplizierter. In diesem Bereich hat .NET wohl den Vorteil, und Java hat, wie mir gesagt wurde, die Oberhand behalten. Für einige "on the bare metal" -Hacker ist es einfach, seinen Code "in der Nähe der Maschine" zu loben. Jetzt ist es jedoch schwieriger, eine bessere Assembly von Hand zu erstellen, als den Compiler seiner Arbeit zu überlassen. Für C ++ wurde der Compiler seit einem Jahrzehnt normalerweise besser als der Hacker. Für C # und Java ist dies noch einfacher.
Der neue Standard C ++ 0x wird C ++ - Compilern jedoch ein einfaches Speichermodell auferlegen, das effektiven Multiprozessor- / Parallel- / Threading-Code in C ++ standardisiert (und damit vereinfacht) und Optimierungen für Compiler einfacher und sicherer macht. Aber dann werden wir in ein paar Jahren sehen, ob seine Versprechen eingehalten werden.
C ++ / CLI vs. C # / VB.NET
Hinweis: In diesem Abschnitt geht es um C ++ / CLI, dh das von .NET gehostete C ++, nicht das native C ++.
Letzte Woche hatte ich eine Schulung zur .NET-Optimierung und stellte fest, dass der statische Compiler sowieso sehr wichtig ist. So wichtig wie JIT.
Der gleiche Code, der in C ++ / CLI kompiliert wurde (oder sein Vorgänger, Managed C ++), kann zeitweise schneller sein als der gleiche Code, der in C # (oder VB.NET, dessen Compiler dieselbe IL als C # erzeugt) erzeugt wurde.
Weil der statische C ++ - Compiler viel besser war, um bereits optimierten Code zu erzeugen als den von C #.
Beispielsweise ist das Inlining von Funktionen in .NET auf Funktionen beschränkt, deren Bytecode kleiner oder gleich 32 Byte lang ist. Ein Code in C # erzeugt also einen 40-Byte-Accessor, der von der JIT niemals eingebunden wird. Der gleiche Code in C ++ / CLI erzeugt einen 20-Byte-Accessor, der von der JIT eingebunden wird.
Ein weiteres Beispiel sind temporäre Variablen, die einfach vom C ++ - Compiler kompiliert werden, während sie in der vom C # -Compiler erstellten IL noch erwähnt werden. Die statische C ++ - Kompilierungsoptimierung führt zu weniger Code und autorisiert somit erneut eine aggressivere JIT-Optimierung.
Es wurde spekuliert, dass der C ++ / CLI-Compiler von den umfangreichen Optimierungstechniken des nativen C ++ - Compilers profitierte.
Fazit
Ich liebe C ++.
Aber meines Erachtens sind C # oder Java insgesamt eine bessere Wahl. Nicht weil sie schneller als C ++ sind, sondern weil sie, wenn Sie ihre Qualitäten addieren, produktiver sind, weniger Schulung benötigen und vollständigere Standardbibliotheken als C ++ haben. Und wie bei den meisten Programmen sind ihre Geschwindigkeitsunterschiede (auf die eine oder andere Weise) vernachlässigbar ...
Bearbeiten (2011-06-06)
Meine Erfahrung mit C # /. NET
Ich habe jetzt 5 Monate fast ausschließlich professionelle C # -Codierung (was zu meinem Lebenslauf führt, der bereits voll mit C ++ und Java und einem Hauch von C ++ / CLI ist).
Ich habe mit WinForms (Ahem ...) und WCF (cool!) Und WPF (Cool !!!! sowohl über XAML als auch über rohes C # gespielt. WPF ist so einfach, dass Swing meiner Meinung nach einfach nicht damit zu vergleichen ist) und C # 4.0.
Die Schlussfolgerung ist, dass es zwar einfacher / schneller ist, einen Code zu erstellen, der in C # / Java funktioniert als in C ++, es jedoch viel schwieriger ist, einen starken, sicheren und robusten Code in C # (und noch schwieriger in Java) als in C ++ zu erstellen. Gründe gibt es zuhauf, aber es kann zusammengefasst werden durch:
- Generika sind nicht so leistungsfähig wie Vorlagen ( versuchen Sie, eine effiziente generische Analysemethode (von Zeichenfolge bis T) oder ein effizientes Äquivalent von boost :: lexical_cast in C # zu schreiben, um das Problem zu verstehen ).
- RAII bleibt unerreicht ( GC kann immer noch auslaufen (ja, ich musste dieses Problem lösen) und behandelt nur den Speicher. Selbst C #
using
ist nicht so einfach und leistungsstark, da das Schreiben korrekter Dispose-Implementierungen schwierig ist ).
- C #
readonly
und Java final
sind nirgends so nützlich wie in C ++const
(es gibt keine Möglichkeit, schreibgeschützte komplexe Daten (z. B. einen Baum von Knoten) in C # ohne großen Aufwand verfügbar zu machen, während dies eine integrierte Funktion von C ++ ist. Unveränderliche Daten sind eine interessante Lösung , aber nicht alles kann unveränderlich gemacht werden, also ist es bei weitem nicht genug ).
C # bleibt also eine angenehme Sprache, solange Sie etwas wollen, das funktioniert, aber eine frustrierende Sprache, sobald Sie etwas wollen, das immer und sicher funktioniert.
Java ist noch frustrierender, da es die gleichen Probleme wie C # hat und mehr: Ohne das Äquivalent des using
Schlüsselworts von C # hat ein sehr erfahrener Kollege von mir zu viel Zeit darauf verwendet, sicherzustellen, dass seine Ressourcen korrekt freigegeben wurden, während dies bei C ++ der Fall wäre war einfach (mit Destruktoren und intelligenten Zeigern).
Ich denke also, dass der Produktivitätsgewinn von C # / Java für die meisten Codes sichtbar ist ... bis zu dem Tag, an dem der Code so perfekt wie möglich sein muss. An diesem Tag wirst du Schmerzen kennen. (Sie werden nicht glauben, was von unseren Server- und GUI-Apps verlangt wird ...).
Informationen zu serverseitigem Java und C ++
Ich hielt Kontakt zu den Serverteams (ich habe 2 Jahre unter ihnen gearbeitet, bevor ich zum GUI-Team zurückkehrte) auf der anderen Seite des Gebäudes und lernte etwas Interessantes.
In den letzten Jahren bestand der Trend darin, dass die Java-Server-Apps die alten C ++ - Server-Apps ersetzen sollten, da Java über viele Frameworks / Tools verfügt und einfach zu warten, bereitzustellen usw. usw. ist.
... Bis das Problem der geringen Latenz in den letzten Monaten seinen hässlichen Kopf aufrichtete. Dann haben die Java-Server-Apps, unabhängig von der von unserem erfahrenen Java-Team versuchten Optimierung, einfach und sauber das Rennen gegen den alten, nicht wirklich optimierten C ++ - Server verloren.
Derzeit besteht die Entscheidung darin, die Java-Server für den allgemeinen Gebrauch zu behalten, wenn die Leistung zwar noch wichtig ist, jedoch nicht vom Ziel mit geringer Latenz betroffen ist, und die bereits schnelleren C ++ - Serveranwendungen aggressiv für Anforderungen mit geringer Latenz und extrem geringer Latenz zu optimieren.
Fazit
Nichts ist so einfach wie erwartet.
Java und noch mehr C # sind coole Sprachen mit umfangreichen Standardbibliotheken und Frameworks, in denen Sie schnell codieren können und sehr bald Ergebnisse erzielen.
Wenn Sie jedoch rohe Leistung, leistungsstarke und systematische Optimierungen, starke Compiler-Unterstützung, leistungsstarke Sprachfunktionen und absolute Sicherheit benötigen, machen es Java und C # schwierig, die letzten fehlenden, aber kritischen Qualitätsprozente zu gewinnen, die Sie benötigen, um sich von der Konkurrenz abzuheben.
Es ist, als ob Sie weniger Zeit und weniger erfahrene Entwickler in C # / Java als in C ++ benötigen, um Code mit durchschnittlicher Qualität zu erstellen. In dem Moment, in dem Sie exzellenten bis perfekten Qualitätscode benötigen, war es plötzlich einfacher und schneller, die Ergebnisse zu erhalten direkt in C ++.
Dies ist natürlich meine eigene Wahrnehmung, die möglicherweise auf unsere spezifischen Bedürfnisse beschränkt ist.
Aber genau das passiert heute, sowohl in den GUI-Teams als auch in den serverseitigen Teams.
Natürlich werde ich diesen Beitrag aktualisieren, wenn etwas Neues passiert.
Bearbeiten (22.06.2011)
"Wir stellen fest, dass C ++ in Bezug auf die Leistung mit großem Vorsprung gewinnt. Es erforderte jedoch auch die umfangreichsten Optimierungsbemühungen, von denen viele auf einem Niveau durchgeführt wurden, das dem durchschnittlichen Programmierer nicht zur Verfügung stehen würde.
[...] Die Java-Version war wahrscheinlich am einfachsten zu implementieren, aber am schwierigsten auf Leistung zu analysieren. Insbesondere die Auswirkungen der Speicherbereinigung waren kompliziert und sehr schwer abzustimmen. "
Quellen:
Bearbeiten (20.09.2011)
"Das Schlagwort bei Facebook ist, dass ' vernünftigerweise geschriebener C ++ - Code nur schnell läuft ', was den enormen Aufwand für die Optimierung von PHP- und Java-Code unterstreicht. Paradoxerweise ist C ++ - Code schwieriger zu schreiben als in anderen Sprachen, aber effizienter Code ist ein viel einfacher [in C ++ als in anderen Sprachen zu schreiben]. "
- Herb Sutter bei // build / unter Angabe von Andrei Alexandrescu
Quellen: