Gibt es einen Leistungsabfall/-einbußen bei der Verwendung einer reinen C-Bibliothek in C++-Code?

Lesezeit: 9 Minuten

Benutzer-Avatar
John Doe

Ich habe diesen Link gesehen, aber ich frage nicht nach einer Leistungsminderung für Code, der “extern” verwendet. Ich meine ohne “extern”, gibt es “Kontextwechsel” bei der Verwendung der C-Bibliothek in C++? Gibt es Probleme bei der Verwendung reiner C-Funktionen (nicht umschlossener Klassen) in einer C++-Anwendung?

  • Nein, es wird keine Leistungseinbußen geben.

    – Steve

    30. Dezember 2017 um 2:30 Uhr

  • C++ baut auf C auf und ist daher abwärtskompatibel.

    – Gary Hayes

    30. Dezember 2017 um 2:31 Uhr

  • Was meinst du mit “ohne extern”? Wie rufen Sie Ihre C-Funktionen aus C++ auf, wenn sie nicht irgendwie deklariert sind extern "C"?

    – BeeOnRope

    30. Dezember 2017 um 3:18 Uhr

  • @GaryHayes Entschuldigung, aber das ist BS. C++ ist nicht „auf C aufgebaut“. Nicht alles C++ ist gültiges C und nicht alles C ist gültiges C++. C++ ist nicht C, also von rückwärts Kompatibilität, als ob C++ C2.0 wäre, ist einfach falsch. Es besteht eine gewisse Kompatibilität zwischen den beiden Sprachen, aber nicht rückwärts Kompatibilität.

    – Bakuriu

    30. Dezember 2017 um 12:31 Uhr

  • @ Lưu Vĩnh Phúc: Das geht etwas am Thema vorbei. Wir sprechen über den Preis eines Funktionsaufrufs, nicht über die Effizienz der Funktion.

    – Steve

    31. Dezember 2017 um 4:43 Uhr

Benutzer-Avatar
Basile Starynkevitch

Sowohl C als auch C++ sind Programmiersprachen Spezifikationen (auf Englisch geschrieben, siehe zB n1570 für die Spezifikation von C11) und sprechen nicht von Performance (sondern vom Verhalten des Programms, also ca Semantik).

Sie werden jedoch wahrscheinlich einen Compiler wie z GCC oder Klirren die keine Leistungseinbußen mit sich bringen, da sie die gleiche Art von interner Zwischendarstellung (z. B. GIMPLE für GCC und LLVM für Clang) für C- und C++-Sprachen erstellen und weil C- und C++-Code kompatibel sind ABIs und Aufruf Konventionen.

In der Praxis extern "C" keine Aufrufkonventionen ändern, sondern deaktivieren Namensverstümmelung. Sein genauer Einfluss auf den Compiler ist jedoch spezifisch für diesen Compiler. Es kann (oder nicht) deaktiviert werden Einfügen (aber bedenke -flto für Link-Time-Optimierung in GCC).

Einige C-Compiler (z winzigecc) erzeugen Code mit schlechter Leistung. Eben GCC oder Klirrenwenn verwendet mit -O0 oder ohne ausdrücklich ermöglichen Optimierung (zB durch Vorbeigehen -O1 oder -O2 usw.) könnte langsamen Code produzieren (und Optimierungen sind damit standardmäßig deaktiviert).

Übrigens wurde C++ so entworfen, dass es mit C interoperabel ist (und diese starke Einschränkung erklärt die meisten Mängel von C++).

In einigen Fällen kann es sich um echten C++-Code handeln leicht schneller als entsprechender echter C-Code. Um beispielsweise ein Array von Zahlen zu sortieren, verwenden Sie std::array und std::sortieren in echtem C++, und die Vergleichsoperationen in der Sortierung werden wahrscheinlich inline ausgeführt. Mit C-Code verwenden Sie einfach qsort und jeder Vergleich durchläuft einen indirekten Funktionsaufruf (da der Compiler kein Inlining durchführt qsortauch wenn es theoretisch möglich wäre…).

In einigen anderen Fällen kann es sich um echten C++-Code handeln leicht Langsamer; zum Beispiel mehrere (aber nicht alle) Implementierungen von ::operator new rufen einfach an malloc (dann Prüfung auf Fehler), aber nicht inline.

In der Praxis gibt es keinen Nachteil beim Aufrufen von C-Code aus C++-Code oder C++-Code aus C-Code, da die Aufrufkonventionen kompatibel sind.

Das C langjmp facility ist wahrscheinlich schneller als das Auslösen von C++-Ausnahmen, aber sie haben nicht die gleiche Semantik (siehe Stapel abwickeln) und longjmp mischt sich nicht gut mit C++-Code.

Wenn Ihnen die Leistung so wichtig ist, schreiben Sie (in echtem C und in echtem C++) zweimal Ihren Code und Benchmark. Sie werden wahrscheinlich eine kleine Änderung (höchstens ein paar Prozent) zwischen C und C++ beobachten Ich würde mich überhaupt nicht stören (und Ihre Leistungsbedenken sind praktisch unbegründet).


Kontextwechsel ist ein verwandtes Konzept Betriebssystem und Multitasking und passiert weiter Prozesse laufend Maschinensprache ausführbar während Vorkaufsrecht. Wie das ausführbar erhalten wird (von einem C-Compiler, von einem C++-Compiler, von einem Go-Compiler, von einem SBCL-Compiler oder als Interpreter einer anderen Sprache wie Perl oder Bytecode-Python) ist völlig irrelevant (da ein Kontextwechsel jederzeit erfolgen kann Maschine Unterricht, während unterbricht). Lesen Sie einige Bücher wie Betriebssysteme: Drei leichte Teile.

  • Ich denke, OP ging davon aus, dass es beim Wechseln von C ++ zu C und umgekehrt zu einem Stapelwechsel kommen könnte. Manchmal ist dies beim Übergang von einer Sprache in eine andere erforderlich, obwohl dies bei C und C++ nicht der Fall ist. Go muss den Stack wahrscheinlich irgendwie wechseln, wenn externer C-Code ausgeführt wird, da es einen segmentierten Stack für Coroutinen verwendet und daher speziell generierten Code in jede Funktion einfügen muss – was bei einer typischen C-Bibliothek nicht der Fall ist.

    – StaceyGirl

    30. Dezember 2017 um 2:52 Uhr


  • AFAIK, Go wechselt in diesem Fall nicht den Stapel, aber selbst wenn dies der Fall wäre, ist es kein Kontextwechsel und würde sehr schnell laufen.

    – Basile Starynkevitch

    30. Dezember 2017 um 2:55 Uhr


  • @BasileStarynkevitch Dies ist im Grunde ein Kontextwechsel im Benutzerbereich. Es ist viel schneller als das Ausschalten des Betriebssystemkontexts. Ich weiß nicht, ob Go das tut, aber es muss irgendwie mit dem Stapelüberlauf in externem C-Code umgehen. Also stürzt es entweder ab oder wechselt den Stapel oder vielleicht etwas dazwischen …?

    – StaceyGirl

    30. Dezember 2017 um 3:00 Uhr


  • Er setzte „Kontextwechsel“ in Anführungszeichen. Ich nahm an, dass er sich auf eine Art Thunk-/Proxy-Code zwischen dem C++-Programm und der C-Bibliothek bezog.

    – Steve

    30. Dezember 2017 um 3:04 Uhr

  • “zum Beispiel mehrere (aber nicht alle) Implementierungen von ::operator new rufen einfach an malloc aber nicht inline sind” Es kann noch viel schlimmer sein: Auf meiner Maschine, an ::operator new() implementiert, um durchzurufen malloc() übertrifft die Norm ::operator new() um etwa hundert CPU-Zyklen pro Aufruf …

    – Cmaster – Wiedereinsetzung von Monica

    30. Dezember 2017 um 4:04 Uhr


Benutzer-Avatar
BeeOnRope

Auf einer grundlegenden Ebene, nein, werden Sie beim Aufrufen einer C-Bibliothek aus C++-Code keine Leistungseinbußen beim „Umschalten“ feststellen. Zum Beispiel sollte das Aufrufen einer C-Methode, die in einer anderen Übersetzungseinheit definiert ist, von C++ ungefähr die gleiche Leistung haben wie das Aufrufen der gleichen Methode, die in C++ (auf die gleiche C-ähnliche Weise) in einer anderen Übersetzungseinheit implementiert ist.

Dies liegt daran, dass gängige Implementierungen von C- und C++-Compilern die Quelle letztendlich in nativen Code kompilieren und eine extern "C" Funktion wird effizient unterstützt mit der gleichen Art von call das kann bei einem C++-Aufruf vorkommen. Die Aufrufkonventionen basieren in der Regel auf der Plattform ABI und sind in beiden Fällen ähnlich.

Abgesehen von dieser grundlegenden Tatsache kann es beim Aufrufen einer C-Funktion immer noch einige Leistungsnachteile geben, im Gegensatz zur Implementierung derselben Funktion in C++:

  • In C implementierte und deklarierte Funktionen extern "C" und die von C++-Code aufgerufen werden, sind normalerweise nicht eingebettet (da sie per Definition nicht in einem Header implementiert sind), was eine ganze Reihe möglicherweise sehr leistungsfähiger Optimierungen verhindert0.
  • Die meisten Datentypen, die in C++-Code verwendet werden1 kann nicht direkt von C-Code verwendet werden, also zum Beispiel, wenn Sie eine haben std::string in Ihrem C++-Code müssen Sie einen anderen Typ auswählen, um ihn an C-Code zu übergeben – char * ist üblich, verliert aber Informationen über die explizite Länge, die langsamer sein kann als eine C++-Lösung. Viele Typen haben kein direktes C-Äquivalent, sodass Sie möglicherweise mit einer kostspieligen Konvertierung stecken bleiben.
  • C-Code verwendet malloc und free für die dynamische Speicherverwaltung, während C++-Code im Allgemeinen verwendet wird new und delete (und zieht es normalerweise vor, diese Anrufe so weit wie möglich hinter anderen Klassen zu verstecken). Wenn Sie Speicher in einer Sprache zuweisen müssen, der in einer anderen freigegeben wird, kann dies zu einer Diskrepanz führen, bei der Sie in die “andere” Sprache zurückrufen müssen, um dies kostenlos zu tun, oder unnötige Kopien usw.
  • C-Code nutzt häufig die Routinen der C-Standardbibliothek, während C++-Code normalerweise Methoden aus der C++-Standardbibliothek verwendet. Da es viele funktionale Überschneidungen gibt, ist es möglich, dass eine Mischung aus C und C++ einen größeren Code-Fußabdruck hat als reiner C++-Code, da mehr C-Bibliotheksmethoden verwendet werden2.

Die obigen Bedenken gelten nur, wenn eine reine C++-Implementierung einer C-Implementierung gegenübergestellt wird, und bedeuten nicht wirklich, dass beim Aufrufen von C eine Leistungsminderung auftritt: Es beantwortet wirklich die Frage „Warum könnte das Schreiben einer Anwendung in einer Mischung aus C und C++ langsamer als reines C++?”. Darüber hinaus sind die obigen Probleme hauptsächlich bei sehr kurzen Anrufen von Belang, bei denen die oben genannten Overheads erheblich sein können. Wenn Sie eine längere Funktion in C aufrufen, ist dies weniger problematisch. Der “Datentyp-Nichtübereinstimmung” könnte Sie immer noch beißen, aber dies kann auf der C++-Seite umgangen werden.


0 Interessanterweise erlaubt die Link-Time-Optimierung tatsächlich, dass C-Methoden in C++-Code eingebettet werden, was ein wenig erwähnter Vorteil von LTO ist. Dies hängt natürlich im Allgemeinen davon ab, dass Sie die C-Bibliothek selbst aus dem Quellcode mit den entsprechenden LTO-Optionen erstellen.

1 ZB so ziemlich alles andere als ein Standard-Layout-Typ.

2 Dies wird zumindest teilweise durch die Tatsache abgemildert, dass viele C++-Standardbibliotheksaufrufe letztendlich an C-Bibliotheksroutinen für das „schwere“ Heben delegieren, wie z. B. how std::copy Anrufe memcpy oder memset wenn möglich und wie am meisten new Implementierungen rufen letztendlich an malloc.

C++ ist seit seiner Einführung stark gewachsen und hat sich stark verändert, aber es ist abwärtskompatibel mit C. C++-Compiler werden im Allgemeinen aus C-Compilern erstellt, aber noch weiter modernisiert Link-Time-Optimierungen. Ich könnte mir vorstellen, dass viele Software C- und C++-Code zuverlässig mischen kann, sowohl in den Benutzerbereichen als auch in den verwendeten Bibliotheken. Ich habe kürzlich eine Frage beantwortet, bei der es darum ging, einen Funktionszeiger einer C++-Klasse an eine C-implementierte Bibliotheksfunktion zu übergeben. Der Poster sagte, es funktionierte für ihn. Es ist also möglich, dass C++ besser mit C kompatibel ist, als Programmierer oder Benutzer denken würden.

C++ arbeitet jedoch in vielen verschiedenen Paradigmen, die C nicht hat, da es objektorientiert ist, und implementiert ein ganzes Spektrum an Abstraktionen, neuen Datentypen und Operatoren. Bestimmte Datentypen sind leicht übersetzbar (char * C-Saite bis a std::string), andere nicht. Dieser Abschnitt auf GNU.org über C++-Compiler-Optionen kann von einigem Interesse sein.

Ich würde mir keine allzu großen Sorgen oder Sorgen über einen Leistungsabfall machen, wenn die beiden Sprachen gemischt werden. Der Endbenutzer und sogar der Programmierer würden kaum messbare Leistungsänderungen bemerken, es sei denn, sie hätten es mit großen Datenabstraktionen zu tun.

1140850cookie-checkGibt es einen Leistungsabfall/-einbußen bei der Verwendung einer reinen C-Bibliothek in C++-Code?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy