Ist C # wirklich langsamer als C ++?

77

Ich habe mich jetzt schon eine Weile über dieses Problem gewundert.

Natürlich gibt es in C # Dinge, die nicht auf Geschwindigkeit optimiert sind. Daher kann die Verwendung dieser Objekte oder Sprachverbesserungen (wie LinQ) dazu führen, dass der Code langsamer wird.

Wenn Sie jedoch keine dieser Optimierungen verwenden, sondern nur dieselben Codeteile in C # und C ++ vergleichen (es ist einfach, sie ineinander zu übersetzen). Wird es wirklich so viel langsamer sein?

Ich habe Vergleiche gesehen, die zeigen, dass C # in einigen Fällen sogar noch schneller sein kann, da der JIT-Compiler theoretisch den Code in Echtzeit optimieren und bessere Ergebnisse erzielen sollte:

Verwaltet oder nicht verwaltet?

Wir sollten uns daran erinnern, dass der JIT-Compiler den Code in Echtzeit kompiliert. Dies ist jedoch ein einmaliger Aufwand. Der gleiche Code (einmal erreicht und kompiliert) muss zur Laufzeit nicht erneut kompiliert werden.

Der GC fügt auch nicht viel Overhead hinzu, es sei denn, Sie erstellen und zerstören Tausende von Objekten (wie die Verwendung von String anstelle von StringBuilder). Und das in C ++ zu tun, wäre auch teuer.

Ein weiterer Punkt, den ich ansprechen möchte, ist die bessere Kommunikation zwischen DLLs, die in .Net eingeführt wurden. Die .Net-Plattform kommuniziert viel besser als verwaltete COM-basierte DLLs.

Ich sehe keinen inhärenten Grund, warum die Sprache langsamer sein sollte, und ich denke nicht wirklich, dass C # langsamer als C ++ ist (sowohl aus Erfahrung als auch aus Mangel an einer guten Erklärung).

Wird ein Teil des gleichen Codes, der in C # geschrieben wurde, langsamer sein als der gleiche Code in C ++?
Wenn ja, warum?

Eine andere Referenz (die ein bisschen darüber spricht, aber keine Erklärung darüber, warum):

Warum sollten Sie C # verwenden, wenn es langsamer als C ++ ist?

Yochai Timmer
quelle
Weil C # viel einfacher zu verwenden ist als C ++, besonders wenn es um die
grafische Benutzeroberfläche
3
Wirklich ... Es kommt darauf an ... Manche Dinge sind schneller, andere langsamer. C / C ++ ist "deterministischer" (kein Garbage Collector auf Ihrem Rücken). Wenn Sie 100 Threads erzeugen möchten, kann ich Ihnen sagen, dass der GC Sie mit seiner Langsamkeit verfolgen wird (und bevor Sie mir sagen, dass 100 Threads zu viele sind, wissen Sie, dass Skype und McAfee AV jetzt jeweils 40 Threads auf meinem PC haben). .. Marschallieren in C # ist ein Schmerz (und es ist langsamer). Die Codierung ist ziemlich schnell. Nein, das ist keine Flamme. Ich bevorzuge wirklich C #.
Xanatos
1
Ich denke, das sollte bearbeitet und wieder geöffnet werden. Ich denke, der Fragesteller versteht möglicherweise nicht, dass eine grundlegendere Frage die Leistungsunterschiede zwischen C # und Sprachen wie C oder C ++ beeinflusst. Dies ist "Was ist der Unterschied zwischen Code, der von einem C-Compiler generiert wird (und wie er ausgeführt wird), und Code, der von der generiert wird?" C # -Compiler? " C # / Java und andere interpretierte oder VM-Sprachen können beim Ausführen ihres Bytecodes mehrere Zwischenschritte aufweisen, die in einem entsprechenden C-Programm vollständig fehlen.
Jonathon Faust
3
Während Sie argumentieren könnten , dass dies (wie gesagt) als "keine echte Frage" qualifiziert ist, ist die Behauptung, es sei subjektiv, unsinnig. "Ist A schneller als B" ist offen für objektive Messungen. Selbst wenn die durch eine solche Messung erhaltene Antwort nicht eindeutig ist (einige Dinge sind in der einen schneller, andere in der anderen), ist sie dennoch objektiv.
Jerry Coffin
1
Siehe die Computersprache Benchmark - Spiel für einen gründlichen Vergleich von vielen Sprachen auf vielen verschiedenen Arten von Problemen.
Assaf Lavie

Antworten:

143

Warnung: Die Frage, die Sie gestellt haben, ist wirklich ziemlich komplex - wahrscheinlich viel komplexer als Sie denken. Infolgedessen ist dies eine sehr lange Antwort.

Aus rein theoretischer Sicht gibt es wahrscheinlich eine einfache Antwort darauf: Es gibt (wahrscheinlich) nichts an C #, was wirklich verhindert, dass es so schnell wie C ++ ist. Trotz der Theorie gibt es jedoch einige praktische Gründe, warum es unter bestimmten Umständen unter bestimmten Umständen langsamer ist .

Ich werde drei grundlegende Unterschiede betrachten: Sprachfunktionen, Ausführung virtueller Maschinen und Speicherbereinigung. Die beiden letzteren gehören oft zusammen, können aber unabhängig voneinander sein, daher werde ich sie separat betrachten.

Sprachmerkmale

C ++ legt großen Wert auf Vorlagen und Funktionen im Vorlagensystem, die größtenteils dazu gedacht sind, beim Kompilieren so viel wie möglich zu tun. Aus Sicht des Programms sind sie daher "statisch". Die Vorlagen-Metaprogrammierung ermöglicht die Ausführung völlig beliebiger Berechnungen zur Kompilierungszeit (dh das Vorlagensystem ist Turing abgeschlossen). Daher kann im Wesentlichen alles, was nicht von Eingaben des Benutzers abhängt, zur Kompilierungszeit berechnet werden. Zur Laufzeit ist es also einfach eine Konstante. Die Eingabe hierfür kann jedoch Dinge wie Typinformationen enthalten. Daher wird ein Großteil dessen, was Sie zur Laufzeit in C # über Reflection tun würden, normalerweise zur Kompilierungszeit über die Metaprogrammierung von Vorlagen in C ++ ausgeführt. Es gibt jedoch definitiv einen Kompromiss zwischen Laufzeitgeschwindigkeit und Vielseitigkeit - was Vorlagen tun können,

Die Unterschiede in den Sprachmerkmalen führen dazu, dass fast jeder Versuch, die beiden Sprachen einfach durch Transliterieren von C # in C ++ (oder umgekehrt) zu vergleichen, wahrscheinlich zu Ergebnissen zwischen bedeutungslos und irreführend führt (und dasselbe gilt für die meisten anderen Sprachpaare auch). Die einfache Tatsache ist, dass für alles, was größer als ein paar Codezeilen oder so ist, fast niemand die Sprachen auf dieselbe Weise (oder nahe genug auf dieselbe Weise) verwenden wird, dass ein solcher Vergleich etwas darüber aussagt, wie diese Sprachen aussehen Arbeit im wirklichen Leben.

Virtuelle Maschine

Wie fast jede einigermaßen moderne VM kann und wird Microsoft for .NET eine JIT-Kompilierung (auch als "dynamisch" bezeichnet) durchführen. Dies stellt jedoch eine Reihe von Kompromissen dar.

In erster Linie ist die Optimierung von Code (wie die meisten anderen Optimierungsprobleme) größtenteils ein NP-vollständiges Problem. Für alles andere als ein wirklich triviales / Spielzeugprogramm ist fast garantiert, dass Sie das Ergebnis nicht wirklich "optimieren" (dh Sie finden nicht das wahre Optimum) - der Optimierer macht den Code einfach besser als er war vorher. Einige bekannte Optimierungen benötigen jedoch viel Zeit (und häufig Speicher) für die Ausführung. Bei einem JIT-Compiler wartet der Benutzer, während der Compiler ausgeführt wird. Die meisten teureren Optimierungstechniken sind ausgeschlossen. Die statische Kompilierung hat zwei Vorteile: Erstens, wenn sie langsam ist (z. B. beim Aufbau eines großen Systems), wird sie normalerweise auf einem Server ausgeführt, und niemandverbringt viel Zeit damit, darauf zu warten. Zweitens kann eine ausführbare Datei generiert werden , einmal , und oft von vielen Menschen genutzt. Der erste minimiert die Kosten für die Optimierung; Die zweite amortisiert die viel geringeren Kosten über eine viel größere Anzahl von Ausführungen.

Wie in der ursprünglichen Frage (und auf vielen anderen Websites) erwähnt, bietet die JIT-Kompilierung die Möglichkeit, die Zielumgebung besser zu kennen, was diesen Vorteil (zumindest theoretisch) ausgleichen sollte. Es steht außer Frage, dass dieser Faktor zumindest einen Teil des Nachteils der statischen Kompilierung ausgleichen kann. Für einige ziemlich spezifische Arten von Code und Zielumgebungen gilt dies möglichüberwiegen sogar die Vorteile der statischen Kompilierung, manchmal ziemlich dramatisch. Zumindest nach meinen Tests und Erfahrungen ist dies jedoch ziemlich ungewöhnlich. Zielabhängige Optimierungen scheinen meist entweder relativ kleine Unterschiede zu machen oder können (ohnehin automatisch) nur auf ziemlich spezifische Arten von Problemen angewendet werden. Offensichtlich würde dies passieren, wenn Sie ein relativ altes Programm auf einem modernen Computer ausführen würden. Ein altes Programm, das in C ++ geschrieben wurde, wäre wahrscheinlich zu 32-Bit-Code kompiliert worden und würde auch auf einem modernen 64-Bit-Prozessor weiterhin 32-Bit-Code verwenden. Ein in C # geschriebenes Programm wäre zu Bytecode kompiliert worden, den die VM dann zu 64-Bit-Maschinencode kompiliert hätte. Wenn dieses Programm einen wesentlichen Vorteil aus der Ausführung als 64-Bit-Code ziehen würde, könnte dies einen erheblichen Vorteil bringen. Für eine kurze Zeit, als 64-Bit-Prozessoren noch relativ neu waren, geschah dies ziemlich oft. Aktueller Code, der wahrscheinlich von einem 64-Bit-Prozessor profitiert, ist normalerweise statisch in 64-Bit-Code kompiliert verfügbar.

Die Verwendung einer VM bietet auch die Möglichkeit, die Cache-Nutzung zu verbessern. Anweisungen für eine VM sind häufig kompakter als native Maschinenanweisungen. Mehr davon können in eine bestimmte Menge an Cache-Speicher passen, sodass Sie bei Bedarf eine bessere Chance haben, dass sich ein bestimmter Code im Cache befindet. Dies kann dazu beitragen, dass die interpretierte Ausführung von VM-Code (in Bezug auf die Geschwindigkeit) wettbewerbsfähiger bleibt, als die meisten Leute anfänglich erwarten würden. Sie können viele Anweisungen auf einer modernen CPU in der von einem benötigten Zeit ausführen Cache - Miss.

Erwähnenswert ist auch, dass sich dieser Faktor zwischen den beiden nicht unbedingt unterscheidet. Nichts hindert (zum Beispiel) einen C ++ - Compiler daran, Ausgaben zu erstellen, die auf einer virtuellen Maschine (mit oder ohne JIT) ausgeführt werden sollen. In der Tat ist Microsoft C ++ / CLI fast das - ein (fast) konformer C ++ - Compiler (wenn auch mit vielen Erweiterungen), der eine Ausgabe erzeugt, die auf einer virtuellen Maschine ausgeführt werden soll.

Das Gegenteil ist auch der Fall: Microsoft verfügt jetzt über .NET Native, das C # (oder VB.NET) -Code in eine native ausführbare Datei kompiliert. Dies bietet eine Leistung, die im Allgemeinen viel mehr wie C ++ ist, aber die Funktionen von C # / VB beibehält (z. B. unterstützt C #, das in nativen Code kompiliert wurde, weiterhin die Reflexion). Wenn Sie leistungsintensiven C # -Code haben, kann dies hilfreich sein.

Müllabfuhr

Nach allem, was ich gesehen habe, würde ich sagen, dass die Speicherbereinigung der am schlechtesten verstandene dieser drei Faktoren ist. Nur als offensichtliches Beispiel wird hier die Frage erwähnt: "GC fügt auch nicht viel Overhead hinzu, es sei denn, Sie erstellen und zerstören Tausende von Objekten [...]". Wenn Sie Tausende von Objekten erstellen und zerstören, ist der Aufwand für die Speicherbereinigung in der Realität im Allgemeinen relativ gering. .NET verwendet einen Generations-Scavenger, bei dem es sich um eine Vielzahl von Kopierkollektoren handelt. Der Garbage Collector arbeitet an "Stellen" (z. B. Registern und Ausführungsstapel), an denen Zeiger / Referenzen bekannt sindzugänglich sein. Anschließend werden diese Zeiger auf Objekte "gejagt", die auf dem Heap zugewiesen wurden. Es untersucht diese Objekte auf weitere Zeiger / Referenzen, bis es allen bis zum Ende einer Kette gefolgt ist und alle Objekte gefunden hat, auf die (zumindest potenziell) zugegriffen werden kann. Im nächsten Schritt werden alle Objekte verwendet, die verwendet werden (oder zumindest verwendet werden könnten ), und der Heap wird komprimiert, indem alle in einen zusammenhängenden Block an einem Ende des im Heap verwalteten Speichers kopiert werden. Der Rest des Speichers ist dann frei (Modulo-Finalizer müssen ausgeführt werden, aber zumindest in gut geschriebenem Code sind sie selten genug, dass ich sie für den Moment ignorieren werde).

Dies bedeutet, dass die Garbage Collection beim Erstellen und Zerstören vieler Objekte nur einen geringen Overhead verursacht. Die Zeit, die ein Speicherbereinigungszyklus benötigt, hängt fast ausschließlich von der Anzahl der Objekte ab, die erstellt, aber nicht zerstört wurden. Die Hauptfolge der Eile beim Erstellen und Zerstören von Objekten ist einfach, dass der GC häufiger ausgeführt werden muss, aber jeder Zyklus immer noch schnell ist. Wenn Sie Objekte erstellen und diese nicht zerstören, wird der GC häufiger ausgeführt und jeder Zyklus ist wesentlich langsamer, da er mehr Zeit damit verbringt, Zeiger auf potenziell lebende Objekte zu verfolgen, und mehr Zeit damit verbringt, noch verwendete Objekte zu kopieren.

Um dem entgegenzuwirken, wird bei der Generationenreinigung davon ausgegangen, dass Objekte, die eine ganze Weile "am Leben" geblieben sind, wahrscheinlich noch eine ganze Weile am Leben bleiben. Auf dieser Grundlage gibt es ein System, in dem Objekte, die eine bestimmte Anzahl von Speicherbereinigungszyklen überstehen, "dauerhaft" werden, und der Speicherbereiniger geht einfach davon aus, dass sie noch verwendet werden. Statt sie bei jedem Zyklus zu kopieren, wird er einfach verlassen sie allein. Dies ist oft genug eine gültige Annahme, dass das Aufräumen von Generationen in der Regel einen erheblich geringeren Overhead hat als die meisten anderen Formen der GC.

"Manuelle" Speicherverwaltung wird oft genauso schlecht verstanden. Nur für ein Beispiel gehen viele Vergleichsversuche davon aus, dass die gesamte manuelle Speicherverwaltung ebenfalls einem bestimmten Modell folgt (z. B. Best-Fit-Zuordnung). Dies ist oft wenig (wenn überhaupt) näher an der Realität als die Überzeugungen vieler Menschen über die Müllabfuhr (z. B. die weit verbreitete Annahme, dass dies normalerweise mithilfe der Referenzzählung erfolgt).

Angesichts der Vielzahl von Strategien sowohl für die Speicherbereinigung als auch für die manuelle Speicherverwaltung ist es ziemlich schwierig, die beiden hinsichtlich der Gesamtgeschwindigkeit zu vergleichen. Der Versuch, die Geschwindigkeit der Zuweisung und / oder Freigabe von Speicher (für sich allein) zu vergleichen, führt fast garantiert zu Ergebnissen, die bestenfalls bedeutungslos und im schlimmsten Fall geradezu irreführend sind.

Bonus-Thema: Benchmarks

Da einige Blogs, Websites, Zeitschriftenartikel usw. behaupten, "objektive" Beweise in die eine oder andere Richtung zu liefern, werde ich auch in diesem Bereich meinen Wert von zwei Cent einsetzen.

Die meisten dieser Benchmarks ähneln den Teenagern, die sich für ein Auto entscheiden, und wer gewinnt, darf beide Autos behalten. Die Websites unterscheiden sich jedoch in einem entscheidenden Punkt: Der Typ, der den Benchmark veröffentlicht, darf beide Autos fahren. Durch einen seltsamen Zufall gewinnt sein Auto immer und alle anderen müssen sich damit zufrieden geben, "vertrau mir, ich habe dein Auto wirklich so schnell gefahren, wie es gehen würde."

Es ist einfach, einen schlechten Benchmark zu schreiben, der Ergebnisse liefert, die so gut wie nichts bedeuten. Fast jeder, der in der Nähe der Fähigkeiten ist, die erforderlich sind, um einen Benchmark zu entwerfen, der irgendetwas Sinnvolles hervorbringt, hat auch die Fähigkeit, einen zu erstellen, der die Ergebnisse liefert, die er sich gewünscht hat. Tatsächlich ist es wahrscheinlich einfacher , Code zu schreiben, um ein bestimmtes Ergebnis zu erzielen, als Code, der wirklich aussagekräftige Ergebnisse liefert.

Wie mein Freund James Kanze es ausdrückte: "Vertraue niemals einem Maßstab, den du nicht selbst gefälscht hast."

Fazit

Es gibt keine einfache Antwort. Ich bin mir ziemlich sicher, dass ich eine Münze werfen könnte, um den Gewinner zu bestimmen, dann eine Zahl zwischen (sagen wir) 1 und 20 für den Prozentsatz auswählen könnte, um den sie gewinnen würde, und einen Code schreiben könnte, der wie ein vernünftiger und fairer Benchmark aussehen würde, und hat diese ausgemachte Sache hervorgebracht (zumindest bei einigen Zielprozessoren - ein anderer Prozessor könnte den Prozentsatz ein wenig ändern).

Wie andere bereits betont haben, ist die Geschwindigkeit für den meisten Code nahezu irrelevant. Die logische Folge , dass (was viel häufiger ignoriert) ist , dass in dem kleinen Code , wo Geschwindigkeit nicht egal, es zählt in der Regel eine Menge . Zumindest meiner Erfahrung nach ist C ++ für den Code, bei dem es wirklich darauf ankommt, fast immer der Gewinner. Es gibt definitiv Faktoren, die C # bevorzugen, aber in der Praxis scheinen sie durch Faktoren aufgewogen zu werden, die C ++ bevorzugen. Sie können sicherlich Benchmarks finden, die das Ergebnis Ihrer Wahl anzeigen, aber wenn Sie echten Code schreiben, können Sie ihn in C ++ fast immer schneller machen als in C #. Es kann (oder auch nicht) mehr Geschick und / oder Mühe erfordern, um zu schreiben, aber es ist praktisch immer möglich.

Jerry Sarg
quelle
6
Bei einem früheren Unternehmen basierten einige unserer Entscheidungen auf Benchmarking, aber das Benchmarking wurde schlecht durchgeführt, weil es das Gesamtbild verfehlte (z. B. Auswirkungen auf den GC) oder nur schlecht durchgeführt wurde. Vorsicht vor Benchmarking ... es ist verlockend, Entscheidungen auf der Grundlage von Dingen zu treffen, die außerhalb des Kontexts ausgeführt werden. Wenn Sie Benchmark machen, dann versuchen Sie es so repräsentativ wie möglich zu machen :)
Mark Simpson
4
IIRC, .NET Laufzeit interpretieren nie, aber immer JIT. Die Optimierung wird sowohl vom Compiler zur Kompilierungszeit (Quellcode zu IL) als auch vom Jitter zur Laufzeit (IL zu nativen Anweisungen) durchgeführt. Der Jitter kann sich dafür entscheiden, nicht zu optimieren, wenn das Jit einer Methode zu lange dauert. Wenn eine vollständige Optimierung erforderlich ist, können Sie NGen verwenden, um bei der Installation ein natives Image zu generieren.
Dudu
2
Ich denke, die Antwort ist eigentlich ganz einfach: Meine Antwort wäre: "Ja, weil C # Sie praktisch dazu zwingt, Datenstrukturen als Referenztypen festzulegen, wodurch mit jeder Wrapping-Schicht eine neue Indirektionsebene hinzugefügt wird, wodurch der Speicherverkehr erhöht und die Speicherlokalität verringert wird. In C ++ bleibt das Objekt unabhängig von der Anzahl der Wrapping-Ebenen, die Sie um ein Objekt legen, an derselben Stelle im Speicher. " Sind Sie einverstanden?
user541686
2
@Mehrdad: Ja und nein. Aus praktischer Sicht wird dies wahrscheinlich häufiger für C ++ sprechen. Gleichzeitig kann GC es einfach machen, Referenzen einfach weiterzugeben, um in vielen Fällen das Kopieren von Daten zu vermeiden.
Jerry Coffin
2
@Mehrdad: Ja - so habe ich mir überlegt, was ich gesagt habe: "In C ++ kann man es fast immer schneller machen als in C #. Das Schreiben kann (oder auch nicht) mehr Geschick und / oder Mühe erfordern. aber es ist praktisch immer möglich. "
Jerry Coffin
41

Weil Sie nicht immer die (und ich diese lose) "schnellste" Sprache verwenden müssen? Ich fahre nicht zur Arbeit in einem Ferrari, nur weil es schneller ist ...

Adam Houldsworth
quelle
17
Ich denke, diese Analogie ist sehr passend. Wenn ich einen Ferrari zur Arbeit fahren würde, würde ich nicht schneller zur Arbeit kommen. Das Auto kann sicherlich schneller fahren als ich, aber die Fähigkeit des Fahrzeugs ist nicht der begrenzende Faktor. Es gibt Geschwindigkeitsbegrenzungen, Verkehr, Straßenzustände usw. Ebenso ist Software normalerweise an Dinge wie Benutzerinteraktion, E / A usw. gebunden. Welche Sprache Sie verwenden, macht in solchen Fällen keinen großen Unterschied.
Fred Larson
3
Nun, aber die Frage war: "Welches der Autos ist ein Ferrari und welches ist Volkswagen?" oder ob beide Ferrari sind oder nicht.
Pepr
@pepr Die Frage in der Überschrift lautet: Ja, aber die Frage am Ende des Beitrags lautet: "Warum sollten Sie einen Volkswagen verwenden, wenn Sie einen Ferrari verwenden können?".
Adam Houldsworth
@ AdamHouldsworth: Ja. Die Antwort kann ähnlich sein - "Kostenangelegenheiten".
Pepr
Nein, ich fahre mit dem Motorrad zur Arbeit, hauptsächlich weil es schneller ist.
CashCow
20

C ++ hat immer einen Vorteil für die Leistung. Mit C # kann ich nicht mit Speicher umgehen und habe buchstäblich jede Menge Ressourcen zur Verfügung, um meine Arbeit zu erledigen.

Was Sie sich fragen müssen, ist mehr darüber, welches Ihnen Zeit spart. Maschinen sind jetzt unglaublich leistungsfähig und der größte Teil Ihres Codes sollte in einer Sprache ausgeführt werden, die es Ihnen ermöglicht, den größten Wert in kürzester Zeit zu erzielen.

Wenn es eine Kernverarbeitung gibt, die in C # viel zu lange dauert, können Sie ein C ++ erstellen und sich mit C # darauf einlassen.

Hören Sie auf, über Ihre Codeleistung nachzudenken. Baue Wert auf.

Maxime Rouiller
quelle
13
+1 für "Denken Sie nicht mehr an Ihre Codeleistung. Beginnen Sie mit dem Wertaufbau."
Thomas Matthews
2
Ich stimme der Entwicklungsgeschwindigkeit in C # zu. Es ist in vielen Fällen viel wertvoller.
Yochai Timmer
Entwickler braucht für jeden Fall eine gute Wahl. In einigen Fällen entspricht C # nicht den Anforderungen. Angenommen, eine rechnerbasierte Anwendung wie Video Converter oder Video Mixer oder 3D-Virtualisierung oder Flow Computation oder ein Spiel . Außerdem müssen Sie beachten, dass die Zielhardware projiziert werden soll. Einige Anwendungen müssen auf einem Low-End-HW ausgeführt werden.
SMMousavi
20

Um 2005 versuchten zwei MS-Leistungsexperten von beiden Seiten des nativen / verwalteten Zauns, dieselbe Frage zu beantworten. Ihre Methode und ihr Prozess sind immer noch faszinierend und die Schlussfolgerungen gelten noch heute - und mir ist kein besserer Versuch bekannt, eine fundierte Antwort zu geben. Sie stellten fest, dass eine Diskussion möglicher Gründe für Leistungsunterschiede hypothetisch und zwecklos ist und eine echte Diskussion eine empirische Grundlage für die tatsächlichen Auswirkungen solcher Unterschiede auf die Welt haben muss .

Der alte New Raymond Chen und Rico Mariani legten Regeln für einen Freundschaftswettbewerb fest. Als Spielzeuganwendungskontext wurde ein Chinesisch / Englisch-Wörterbuch ausgewählt: einfach genug, um als Hobby-Nebenprojekt codiert zu werden, aber komplex genug, um nicht triviale Datennutzungsmuster zu demonstrieren. Die Regeln begannen einfach: Raymond codierte eine unkomplizierte C ++ - Implementierung, Rico migrierte sie Zeile für Zeile ohne jegliche Raffinesse nach C # , und beide Implementierungen führten einen Benchmark durch. Anschließend folgten mehrere Optimierungsiterationen.

Die vollständigen Details finden Sie hier: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 .

Dieser Dialog der Titanen ist außergewöhnlich lehrreich und ich empfehle von ganzem Herzen, einzutauchen - aber wenn Ihnen die Zeit oder die Geduld fehlt, hat Jeff Atwood das Fazit wunderbar zusammengestellt :

Geben Sie hier die Bildbeschreibung ein

Schließlich war C ++ 2x schneller - aber anfangs war es 13x langsamer.

Wie Rico zusammenfasst :

Schäme ich mich für meine vernichtende Niederlage? Kaum. Der verwaltete Code erzielte ohne großen Aufwand ein sehr gutes Ergebnis. Um die verwaltete Version zu besiegen, musste Raymond:

  • Schreibe seine eigene Datei / io Zeug

  • Schreiben Sie seine eigene String-Klasse

  • Schreiben Sie seinen eigenen Allokator

  • Schreiben Sie sein eigenes internationales Mapping

Natürlich hat er dafür verfügbare Bibliotheken niedrigerer Ebenen verwendet, aber das ist immer noch viel Arbeit. Können Sie aufrufen, was noch ein STL-Programm übrig hat? Das glaube ich nicht.

Das ist noch meine Erfahrung, 11 Jahre und wer weiß, wie viele C # / C ++ - Versionen später.

Das ist natürlich kein Zufall, denn diese beiden Sprachen erreichen auf spektakuläre Weise ihre sehr unterschiedlichen Designziele. C # möchte dort eingesetzt werden, wo die Entwicklungskosten im Vordergrund stehen (immer noch der Großteil der Software), und C ++ scheint dort, wo Sie keine Kosten sparen würden, um die letzte Unze Leistung aus Ihrem Computer herauszuholen: Spiele, Algo-Handel, Daten- Zentren usw.

Ofek Shilon
quelle
1
Dies ist die beste Antwort, und er fasst wunderschön zusammen!
Rodney P. Barbati
6

C # ist schneller als C ++. Schneller zu schreiben. Für Ausführungszeiten ist nichts besser als ein Profiler.

C # verfügt jedoch nicht über so viele Bibliotheken, wie C ++ problemlos verbinden kann.

Und C # hängt stark von Windows ab ...

Alexandre C.
quelle
5
C # erfordert viel mehr Unterstützung vom Compiler, vom Betriebssystem und von den Plattformen. Ein RIESIGES Problem für eingebettete Systeme.
Thomas Matthews
C # läuft nicht nur unter Windows, sondern auch unter Mac, Linux, Android, iOS, ... siehe microsoft.com/net/multiple-platform-support , xamarin.com/platform , mono-project.com/docs/about-mono/ unterstützte Plattformen .
Jojo
@yoyo: C # läuft auf anderen Plattformen (und es gab auch Mono unter Linux), die Bibliotheken, die Sie möglicherweise verwenden möchten (z. B. Windows Forms), jedoch möglicherweise nicht. Portabilität ist ein Problem für die C # -Entwicklung.
Alexandre C.
Diese Antwort hat sich jetzt geändert. Die .NET Standard-Laufzeit wird derzeit offiziell von Microsoft für die Ausführung unter Linux unterstützt. Wenn Sie also keine Mikrocontroller entwickeln müssen, ist die Portabilität überhaupt kein Problem. Ganz zu schweigen davon, dass es auch viel schneller wurde.
MovGP0
3

Übrigens werden zeitkritische Anwendungen nicht in C # oder Java codiert, hauptsächlich aufgrund der Unsicherheit, wann die Garbage Collection durchgeführt wird.

In der heutigen Zeit ist die Anwendungs- oder Ausführungsgeschwindigkeit nicht mehr so ​​wichtig wie zuvor. Entwicklungspläne, Korrektheit und Robustheit haben höhere Priorität. Eine Hochgeschwindigkeitsversion einer Anwendung ist nicht gut, wenn sie viele Fehler aufweist, viel oder noch schlimmer abstürzt, die Gelegenheit verpasst, auf den Markt zu kommen oder bereitgestellt zu werden.

Da Entwicklungspläne Priorität haben, kommen neue Sprachen heraus, die die Entwicklung beschleunigen. C # ist eine davon. C # unterstützt auch die Korrektheit und Robustheit, indem Features aus C ++ entfernt werden, die häufig auftretende Probleme verursachen: Ein Beispiel sind Zeiger.

Die Unterschiede in der Ausführungsgeschwindigkeit einer mit C # entwickelten und einer mit C ++ entwickelten Anwendung sind auf den meisten Plattformen vernachlässigbar. Dies liegt an der Tatsache, dass die Ausführungsengpässe nicht sprachabhängig sind, sondern normalerweise vom Betriebssystem oder der E / A abhängen. Wenn C ++ beispielsweise eine Funktion in 5 ms ausführt, C # jedoch 2 ms verwendet und das Warten auf Daten 2 Sekunden dauert, ist die in der Funktion verbrachte Zeit im Vergleich zur Wartezeit auf Daten unbedeutend.

Wählen Sie eine Sprache, die für Entwickler, Plattform und Projekte am besten geeignet ist. Arbeiten Sie auf die Ziele Korrektheit, Robustheit und Einsatz hin. Die Geschwindigkeit einer Anwendung sollte als Fehler behandelt werden: Priorisieren Sie sie, vergleichen Sie sie mit anderen Fehlern und beheben Sie sie nach Bedarf.

Thomas Matthews
quelle
Mein Verständnis ist, dass C # (auch Java?) Die Existenz / das Konzept von Zeigern nicht "entfernt", sondern sie durch eine Art Abstraktion über die eigentliche Syntax vor dem Programmierer verbirgt. Dies ist jedoch ein ziemlich belangloser Punkt. Die relative Einfachheit von C # ist unbestritten.
Mike G
3

Eine bessere Sichtweise ist, dass alles langsamer ist als C / C ++, da es abstrahiert, anstatt dem Stick- und Mud-Paradigma zu folgen. Es heißt Systemprogrammierung aus einem Grund, Sie programmieren gegen das Korn oder Bare Metal. Dadurch erhalten Sie auch eine Geschwindigkeit, die Sie mit anderen Sprachen wie C # oder Java nicht erreichen können. Aber leider geht es bei C-Roots nur darum, Dinge auf die harte Tour zu machen, sodass Sie meistens mehr Code schreiben und mehr Zeit damit verbringen, ihn zu debuggen.

C unterscheidet auch zwischen Groß- und Kleinschreibung. Auch Objekte in C ++ folgen strengen Regelsätzen. Beispiel: Eine lila Eistüte ist möglicherweise nicht mit einer blauen Eistüte identisch. Obwohl es sich um Tüten handelt, gehören sie möglicherweise nicht unbedingt zur Kegelfamilie. Wenn Sie vergessen, zu definieren, um welche Tüte es sich handelt, sind Sie fehlerhaft. Somit können die Eigenschaften von Eiscreme Klone sein oder nicht. Das Geschwindigkeitsargument C / C ++ verwendet nun den Stack-and-Heap-Ansatz. Hier erhält Bare Metal sein Metall.

Mit der Boost-Bibliothek können Sie unglaubliche Geschwindigkeiten erreichen. Leider halten sich die meisten Spielestudios an die Standardbibliothek. Der andere Grund dafür könnte sein, dass in C / C ++ geschriebene Software in der Regel eine enorme Dateigröße aufweist, da es sich um eine riesige Sammlung von Dateien anstelle einer einzelnen Datei handelt. Beachten Sie auch, dass alle Betriebssysteme in C geschrieben sind. Warum müssen wir also im Allgemeinen die Frage stellen, was schneller sein könnte?!

Auch das Caching ist nicht schneller als die reine Speicherverwaltung, tut mir leid, aber das fächert sich einfach nicht auf. Speicher ist etwas Physisches, Caching ist etwas, was Software tut, um die Leistung zu steigern. Man könnte auch argumentieren, dass Caching ohne physischen Speicher einfach nicht existieren würde. Die Tatsache, dass der Speicher auf einer bestimmten Ebene verwaltet werden muss, sei es automatisiert oder manuell, wird dadurch nicht ungültig.

Justin
quelle
1

Warum sollten Sie eine kleine Anwendung schreiben, die in C ++ nicht viel Optimierung erfordert, wenn es eine schnellere Route (C #) gibt?

Bloodyaugust
quelle
Warum? Weil ich ein Trottel bin, wenn ich sehe, wann mein Code läuft. Einschließlich Destruktoren. Selbst eine kleine Anwendung profitiert von der Optimierung der Lesbarkeit.
Unsichtbarer
1

Eine genaue Antwort auf Ihre Frage ist nur möglich, wenn Sie Benchmarks für bestimmte Systeme durchführen. Es ist jedoch immer noch interessant, über einige grundlegende Unterschiede zwischen Programmiersprachen wie C # und C ++ nachzudenken.

Zusammenstellung

Das Ausführen von C # -Code erfordert einen zusätzlichen Schritt, in dem der Code JIT'ed ist. In Bezug auf die Leistung wird dies zugunsten von C ++ sein. Außerdem kann der JIT-Compiler den generierten Code nur innerhalb der JIT-Codeeinheit (z. B. einer Methode) optimieren, während ein C ++ - Compiler über Methodenaufrufe hinweg mit aggressiveren Techniken optimieren kann.

Der JIT-Compiler kann den generierten Maschinencode jedoch so optimieren, dass er genau mit der zugrunde liegenden Hardware übereinstimmt, sodass er zusätzliche Hardwarefunktionen nutzen kann, sofern diese vorhanden sind. Meines Wissens macht der .NET JIT-Compiler das nicht, aber er könnte möglicherweise anderen Code für Atom generieren als Pentium-CPUs.

Speicherzugriff

Die Garbage Collected-Architektur kann in vielen Fällen optimalere Speicherzugriffsmuster als Standard-C ++ - Code erstellen. Wenn der für die erste Generation verwendete Speicherbereich klein genug ist, kann er im CPU-Cache verbleiben und die Leistung erhöhen. Wenn Sie viele kleine Objekte erstellen und zerstören, ist der Aufwand für die Wartung des verwalteten Heaps möglicherweise geringer als für die C ++ - Laufzeit erforderlich. Dies hängt wiederum stark von der Anwendung ab. Eine Studie zur Leistung von Python zeigt, dass eine bestimmte verwaltete Python-Anwendung aufgrund optimaler Speicherzugriffsmuster viel besser skaliert werden kann als die kompilierte Version.

Martin Liversage
quelle
Es gibt einen zusätzlichen Schritt für die JIT, um den Code zu kompilieren, aber aufgrund der Regel der Wiederverwendung werden Sie diesen Code wahrscheinlich 10000-mal häufiger verwenden als diese einzelne Kompilierung. (Beispiel: Schleifen). Es ist also eine Frage, ob die JIT gute Arbeit geleistet hat, um diese erste Kompilierung zu kompensieren.
Yochai Timmer
0

Lass dich nicht verwirren!

  • Wenn eine C # -Anwendung im besten Fall und eine C ++ - Anwendung im besten Fall geschrieben wird, ist C ++ schneller.
    Viele Gründe sprechen dafür, warum C ++ von Natur aus schneller ist als C #, z. B. wenn C # eine virtuelle Maschine verwendet, die JVM in Java ähnelt. Grundsätzlich hat eine höhere Sprache eine geringere Leistung (wenn sie im besten Fall verwendet wird).

  • Wenn Sie ein erfahrener professioneller C # -Programmierer sind, genau wie Sie ein erfahrener professioneller C ++ - Programmierer sind, ist die Entwicklung einer Anwendung mit C # viel einfacher und schneller als mit C ++.

Viele andere Situationen zwischen diesen Situationen sind möglich. Sie können beispielsweise eine C # -Anwendung und eine C ++ - Anwendung schreiben, sodass die C # -Anwendung schneller als eine C ++ - Anwendung ausgeführt wird.

Bei der Auswahl einer Sprache sollten Sie die Umstände des Projekts und seines Themas beachten. Für ein allgemeines Geschäftsprojekt sollten Sie C # verwenden. Für ein Projekt mit hoher Leistung wie einen Videokonverter oder ein Bildverarbeitungsprojekt sollten Sie C ++ wählen.

Aktualisieren:

IN ORDNUNG. Vergleichen wir einen praktischen Grund, warum die höchstmögliche Geschwindigkeit von C ++ mehr als C # ist. Betrachten Sie eine gut geschriebene C # -Anwendung und dieselbe C ++ - Version:

  • C # verwendet eine VM als mittlere Schicht zum Ausführen der Anwendung. Es hat Overhead.
  • AFAIK CLR konnte nicht alle C # -Codes im Zielcomputer optimieren. Die C ++ - Anwendung konnte mit den meisten Optimierungen auf dem Zielcomputer kompiliert werden.
  • In C # bedeutet die bestmögliche Optimierung für die Laufzeit die bestmögliche schnelle VM. VM hat sowieso Overhead.
  • C # ist eine höhere Sprache und generiert daher mehr Programmcodezeilen für den endgültigen Prozess. (Beachten Sie den Unterschied zwischen einer Assembly-Anwendung und Ruby. Die gleiche Bedingung besteht zwischen C ++ und einer höheren Sprache wie C # / Java.)

Wenn Sie lieber ein paar mehr Infos in der Praxis als Experten bekommen, sieht dies . Es geht um Java, aber es gilt auch für C #.

SMMousavi
quelle
0

Das Hauptanliegen wäre nicht die Geschwindigkeit, sondern die Stabilität über Windows-Versionen und Upgrades hinweg. Win32 ist in allen Windows-Versionen weitgehend immun und daher sehr stabil.

Wenn Server außer Betrieb genommen und Software migriert werden, kommt es bei allem, was .NET verwendet, zu großer Besorgnis und normalerweise zu großer Panik über .net-Versionen, aber eine vor 10 Jahren erstellte Win32-Anwendung läuft einfach weiter, als wäre nichts passiert.

William Egge
quelle
-1

Ich habe mich seit ungefähr 15 Jahren auf die Optimierung spezialisiert und schreibe regelmäßig C ++ - Code neu, wobei ich die Compiler-Eigenschaften so weit wie möglich nutze, da die C ++ - Leistung oft nicht annähernd der Leistung der CPU entspricht. Die Cache-Leistung muss häufig berücksichtigt werden. Viele Anweisungen zur Vektormathematik sind erforderlich, um den Standard-C ++ - Gleitkommacode zu ersetzen. Ein Großteil des STL-Codes wird neu geschrieben und läuft oft um ein Vielfaches schneller. Mathematik und Code, die Daten stark nutzen, können mit spektakulären Ergebnissen neu geschrieben werden, wenn sich die CPU ihrer optimalen Leistung nähert.

Nichts davon ist in C # möglich. Ihre relative # Echtzeit # -Leistung zu vergleichen, ist wirklich eine erstaunlich ignorante Frage. Der schnellste Code in C ++ ist, wenn jeder einzelne Assembler-Befehl für die jeweilige Aufgabe optimiert ist, ohne unnötige Anweisungen - überhaupt nicht. Wo jedes Speicherelement verwendet wird, wenn es benötigt wird, und nicht n-mal kopiert wird, da dies für das Sprachdesign erforderlich ist. Wo jede erforderliche Speicherbewegung im Einklang mit dem Cache arbeitet. Wo der endgültige Algorithmus nicht verbessert werden kann, basierend auf den genauen Echtzeitanforderungen, unter Berücksichtigung von Genauigkeit und Funktionalität.

Dann nähern Sie sich einer optimalen Lösung.

C # mit dieser idealen Situation zu vergleichen, ist erstaunlich. C # kann nicht mithalten. Tatsächlich schreibe ich gerade eine ganze Reihe von C # -Code neu (wenn ich sage, dass ich neu schreibe, meine ich, ihn vollständig zu entfernen und zu ersetzen), weil er sich nicht einmal in derselben Stadt befindet, geschweige denn in Echtzeit, wenn es um schweres Heben geht Performance.

Also bitte hör auf, dich selbst zu täuschen. C # ist langsam. Schneckentempo. Die gesamte Software verlangsamt sich und C # verschlechtert diesen Geschwindigkeitsabfall. Alle Software wird mit dem Abruf-Ausführungszyklus im Assembler ausgeführt (Sie wissen, auf der CPU). Sie verwenden zehnmal so viele Anweisungen. es wird 10 mal so langsam gehen. Sie verkrüppeln den Cache; es wird noch langsamer gehen. Wenn Sie einer Echtzeitsoftware Garbage Collect hinzufügen, werden Sie oft getäuscht zu denken, dass der Code "ok" läuft. Es gibt nur wenige Momente, in denen der Code "für eine Weile etwas langsam" wird.

Fügen Sie dem Code ein Speicherbereinigungssystem hinzu, bei dem jeder Zyklus zählt. Ich frage mich, ob die Börsenhandelssoftware über eine Speicherbereinigung verfügt (wissen Sie - auf dem System, das mit dem neuen Unterseekabel läuft, das 300 Millionen US-Dollar kostet?). Können wir alle 2 Sekunden 300 Millisekunden sparen? Was ist mit der Flugsteuerungssoftware auf dem Space Shuttle - ist GC dort in Ordnung? Wie wäre es mit Motormanagementsoftware in Hochleistungsfahrzeugen? (Wo der Sieg in einer Saison Millionen wert sein kann).

Die Speicherbereinigung in Echtzeit ist ein vollständiger Fehler.

Nein, C ++ ist nachdrücklich viel schneller. C # ist ein Rücksprung.

David Lloyd
quelle
1
C # ist kein Rücksprung, da C # mit völlig anderen Anforderungen als C ++ entwickelt wurde. C # tauscht die Ausführungsgeschwindigkeit gegen die Entwicklungsgeschwindigkeit. Alle Beispiele, in denen Sie darauf hingewiesen haben, dass C # fehlschlagen würde (Space Shuttles, Motormanagement usw.), waren niemals Zielanwendungen von C #. Mit C # kann das fertige Produkt relativ schnell geliefert werden. Mit C # kann das Produkt auf jeder Hardware ausgeführt werden, auf der seine virtuelle Maschine ausgeführt wird. Können Sie Assembly-Optimierungen schreiben, wenn Sie wissen, dass Ihr Code auf hundert verschiedenen Hardwarekonfigurationen ausgeführt werden muss?
mag_zbc
Ich stimme dem zu, was Sie sagen, mag_zbc, aber die ursprüngliche Frage war "Ist C # wirklich langsamer als C ++?" Und mein Kommentar "Rücksprung" bezog sich auf die Geschwindigkeit von C #, nicht auf die Portabilität.
David Lloyd
Dies ist eine voreingenommene Sichtweise, die auf rein spekulativen Annahmen basiert, ohne Benchmarks bereitzustellen.
zmechanic