Warum ist die anfängliche Zuordnung von C++ so viel größer als die von C?
Lesezeit: 7 Minuten
Rerumu
Wenn Sie denselben Code verwenden, ändert sich durch einfaches Ändern des Compilers (von einem C-Compiler zu einem C++-Compiler), wie viel Speicher zugewiesen wird. Ich bin mir nicht ganz sicher, warum das so ist und würde es gerne besser verstehen. Bisher ist die beste Antwort, die ich erhalten habe, “wahrscheinlich die E/A-Streams”, was nicht sehr beschreibend ist und mich über den Aspekt “Sie zahlen nicht für das, was Sie nicht verwenden” von C++ wundern lässt.
Ich verwende die Clang- und GCC-Compiler, Versionen 7.0.1-8 bzw. 8.3.0-6. Mein System läuft auf Debian 10 (Buster), spätestens. Die Benchmarks werden über das Valgrind-Massiv durchgeführt.
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
Der verwendete Code ändert sich nicht, aber ob ich als C oder als C++ kompiliere, es ändert die Ergebnisse des Valgrind-Benchmarks. Die Werte bleiben jedoch über Compiler hinweg konsistent. Die Laufzeitbelegungen (Peak) für das Programm sehen wie folgt aus:
Für Valgrind laufe ich valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-lang auf jedem Compiler und Sprache, dann ms_print zur Anzeige der Peaks.
Mache ich hier etwas falsch?
Zunächst, wie baust du? Welche Möglichkeiten nutzen Sie? Und wie misst du? Wie führt man Valgrind?
– Irgendein Programmierer-Typ
20. Juni 2019 um 18:45 Uhr
Wenn ich mich richtig erinnere, haben moderne C++-Compiler ein Ausnahmemodell, bei dem es keine Leistungseinbußen gibt, wenn man a eingibt try Block auf Kosten eines größeren Speicherbedarfs, vielleicht mit einer Sprungtabelle oder so. Versuchen Sie vielleicht, ohne Ausnahmen zu kompilieren, und sehen Sie, welche Auswirkungen das hat. Bearbeiten: Versuchen Sie tatsächlich, verschiedene C++-Funktionen iterativ zu deaktivieren, um zu sehen, welche Auswirkungen dies auf den Speicherbedarf hat.
– Francois Andrieux
20. Juni 2019 um 18:48 Uhr
Beim Kompilieren mit clang++ -xc Anstatt von clangwar die gleiche Zuordnung vorhanden, was stark darauf hindeutet, dass sie auf verknüpfte Bibliotheken zurückzuführen ist
– Justin
20. Juni 2019 um 18:51 Uhr
@bigwillydos Dies ist in der Tat C ++, ich sehe keinen Teil der C ++ – Spezifikationen, die es bricht … Abgesehen davon, dass möglicherweise stdio.h anstelle von cstdio enthalten ist, ist dies jedoch zumindest in älteren C ++ – Versionen zulässig. Was ist Ihrer Meinung nach in diesem Programm “fehlerhaft”?
– Wertigkeit
21. Juni 2019 um 3:34 Uhr
Ich finde es verdächtig, dass diese gcc- und clang-Compiler genau die gleiche Anzahl von Bytes erzeugen C Modus und genau die gleiche Anzahl von Bytes C++ Modus. Ist Ihnen ein Übertragungsfehler unterlaufen?
– RonJohn
22. Juni 2019 um 0:13 Uhr
Nikos C.
Die Heap-Nutzung stammt aus der C++-Standardbibliothek. Es weist beim Start Speicher für die Verwendung der internen Bibliothek zu. Wenn Sie nicht dagegen verlinken, sollte es keinen Unterschied zwischen der C- und der C++-Version geben. Mit GCC und Clang können Sie die Datei kompilieren mit:
g++ -Wl,--as-needed main.cpp
Dadurch wird der Linker angewiesen, nicht mit unbenutzten Bibliotheken zu verknüpfen. In Ihrem Beispielcode wird die C++-Bibliothek nicht verwendet, daher sollte sie nicht mit der C++-Standardbibliothek verknüpft werden.
Sie können dies auch mit der C-Datei testen. Wenn Sie kompilieren mit:
gcc main.c -lstdc++
Die Heap-Nutzung wird wieder angezeigt, obwohl Sie ein C-Programm erstellt haben.
Die Heap-Nutzung hängt offensichtlich von der spezifischen C++-Bibliotheksimplementierung ab, die Sie verwenden. In Ihrem Fall ist das die GNU C++-Bibliothek, libstdc++. Andere Implementierungen weisen möglicherweise nicht die gleiche Speichermenge oder gar keinen Speicher zu (zumindest nicht beim Start). Die LLVM-C++-Bibliothek (libc++) führt zum Beispiel beim Start keine Heap-Zuweisung durch, zumindest nicht auf meinem Linux-Rechner:
clang++ -stdlib=libc++ main.cpp
Die Heap-Nutzung ist dasselbe, als würde man überhaupt nicht dagegen verlinken.
(Wenn die Kompilierung fehlschlägt, ist libc++ wahrscheinlich nicht installiert. Der Paketname enthält normalerweise „libc++“ oder „libcxx“.)
Als ich diese Antwort sehe, ist mein erster Gedanke: “Wenn dieses Flag dazu beiträgt, unnötigen Overhead zu reduzieren, warum ist es dann nicht standardmäßig aktiviert?“. Gibt es darauf eine gute Antwort?
– Nat
21. Juni 2019 um 3:48 Uhr
@Nat Meine Vermutung ist, dass es zur Entwicklungszeit langsamer zu kompilieren ist. Wenn Sie bereit sind, einen Release-Build zu erstellen, würden Sie ihn dann aktivieren. Auch in einer normalen/großen Codebasis kann der Unterschied minimal sein (wenn Sie viele STD-Bibliotheken usw. verwenden).
– DarcyThomas
21. Juni 2019 um 5:04 Uhr
@Nat Die -Wl,--as-needed Flag entfernt Bibliotheken, die Sie in Ihrer angeben -l Flags, aber Sie verwenden sie nicht wirklich. Wenn Sie also keine Bibliothek verwenden, dann verlinken Sie einfach nicht dagegen. Sie brauchen dieses Flag dafür nicht. Wenn Ihr Build-System jedoch zu viele Bibliotheken hinzufügt und es eine Menge Arbeit wäre, sie alle zu bereinigen und nur die benötigten zu verknüpfen, können Sie stattdessen dieses Flag verwenden. Die Standardbibliothek ist jedoch eine Ausnahme, da sie automatisch gelinkt wird. Das ist also ein Eckfall.
– Nikos C.
21. Juni 2019 um 7:03 Uhr
@Nat –as-needed kann unerwünschte Nebeneffekte haben, es überprüft, ob Sie ein Symbol einer Bibliothek verwenden, und wirft diejenigen aus, die den Test nicht bestehen. ABER: Eine Bibliothek könnte auch implizit verschiedene Dinge tun, wenn Sie beispielsweise eine statische C++-Instanz in der Bibliothek haben, wird ihr Konstruktor automatisch aufgerufen. Es gibt seltene Fälle, in denen eine Bibliothek, die Sie nicht explizit aufrufen, erforderlich ist, aber sie existiert.
– Norbert Lange
21. Juni 2019 um 8:16 Uhr
@NikosC. Buildsysteme wissen nicht automatisch, welche Symbole Ihre Anwendung verwendet und welche Bibliotheken sie implementieren (variiert zwischen Compilern, Archs, Distributionen und c/c++-Bibliotheken). Das richtig zu machen, ist ziemlich mühsam, zumindest für die Basis-Laufzeitbibliotheken. Aber für die seltenen Fälle, in denen Sie eine Bibliothek benötigen, sollten Sie einfach –no-as-needed für diese verwenden und –as-needed überall sonst lassen. Ein Anwendungsfall, den ich gesehen habe, sind Bibliotheken zum Verfolgen/Debuggen (lttng) und Bibliotheken, die so etwas wie Authentifizieren/Verbinden tun.
– Norbert Lange
21. Juni 2019 um 9:04 Uhr
Weder GCC noch Clang sind Compiler – sie sind eigentlich Toolchain-Treiberprogramme. Das heißt, sie rufen den Compiler, den Assembler und den Linker auf.
Wenn Sie Ihren Code mit einem C- oder C++-Compiler kompilieren, erhalten Sie dieselbe Assembly. Der Assembler erzeugt die gleichen Objekte. Der Unterschied besteht darin, dass der Toolchain-Treiber dem Linker unterschiedliche Eingaben für die beiden unterschiedlichen Sprachen bereitstellt: unterschiedliche Startups (C++ erfordert Code zum Ausführen von Konstruktoren und Destruktoren für Objekte mit statischer oder Thread-lokaler Speicherdauer auf Namespace-Ebene und erfordert Infrastruktur für Stack Rahmen, um zum Beispiel das Entladen während der Ausnahmeverarbeitung zu unterstützen), die C++-Standardbibliothek (die auch Objekte mit statischer Speicherdauer auf Namespace-Ebene hat) und wahrscheinlich zusätzliche Laufzeitbibliotheken (zum Beispiel libgcc mit seiner Infrastruktur zum Entladen des Stapels).
Kurz gesagt, es ist nicht der Compiler, der die Zunahme des Platzbedarfs verursacht, sondern das Einbinden von Dingen, die Sie verwenden möchten, indem Sie die Sprache C++ auswählen.
Es ist wahr, dass C++ die Philosophie „zahle nur für das, was du verwendest“ verfolgt, aber wenn du die Sprache verwendest, bezahlst du dafür. Sie können Teile der Sprache (RTTI, Ausnahmebehandlung) deaktivieren, aber dann verwenden Sie C++ nicht mehr. Wie in einer anderen Antwort erwähnt, können Sie, wenn Sie die Standardbibliothek überhaupt nicht verwenden, den Treiber anweisen, dies wegzulassen (–Wl,–as-needed), aber wenn Sie keine der Funktionen verwenden werden von C++ oder seiner Bibliothek, warum wählen Sie überhaupt C++ als Programmiersprache?
Die Tatsache, dass die Aktivierung der Ausnahmebehandlung Kosten verursacht, selbst wenn Sie sie nicht wirklich verwenden, ist ein Problem. Das ist für C++-Features im Allgemeinen nicht normal, und es ist etwas, das die C++-Standardarbeitsgruppen versuchen, Wege zu finden, es zu beheben. Siehe den Keynote-Vortrag von Herb Sutter auf der ACCU 2019 C++ defragmentieren: Ausnahmen erschwinglicher und benutzerfreundlicher machen. In aktuellem C++ ist dies jedoch eine unglückliche Tatsache. Und traditionelle C++-Ausnahmen werden wahrscheinlich immer diese Kosten haben, selbst wenn/wenn neue Mechanismen für neue Ausnahmen mit einem Schlüsselwort hinzugefügt werden.
– Peter Cordes
26. Juni 2019 um 3:52 Uhr
14236800cookie-checkWarum ist die anfängliche Zuordnung von C++ so viel größer als die von C?yes
Zunächst, wie baust du? Welche Möglichkeiten nutzen Sie? Und wie misst du? Wie führt man Valgrind?
– Irgendein Programmierer-Typ
20. Juni 2019 um 18:45 Uhr
Wenn ich mich richtig erinnere, haben moderne C++-Compiler ein Ausnahmemodell, bei dem es keine Leistungseinbußen gibt, wenn man a eingibt
try
Block auf Kosten eines größeren Speicherbedarfs, vielleicht mit einer Sprungtabelle oder so. Versuchen Sie vielleicht, ohne Ausnahmen zu kompilieren, und sehen Sie, welche Auswirkungen das hat. Bearbeiten: Versuchen Sie tatsächlich, verschiedene C++-Funktionen iterativ zu deaktivieren, um zu sehen, welche Auswirkungen dies auf den Speicherbedarf hat.– Francois Andrieux
20. Juni 2019 um 18:48 Uhr
Beim Kompilieren mit
clang++ -xc
Anstatt vonclang
war die gleiche Zuordnung vorhanden, was stark darauf hindeutet, dass sie auf verknüpfte Bibliotheken zurückzuführen ist– Justin
20. Juni 2019 um 18:51 Uhr
@bigwillydos Dies ist in der Tat C ++, ich sehe keinen Teil der C ++ – Spezifikationen, die es bricht … Abgesehen davon, dass möglicherweise stdio.h anstelle von cstdio enthalten ist, ist dies jedoch zumindest in älteren C ++ – Versionen zulässig. Was ist Ihrer Meinung nach in diesem Programm “fehlerhaft”?
– Wertigkeit
21. Juni 2019 um 3:34 Uhr
Ich finde es verdächtig, dass diese gcc- und clang-Compiler genau die gleiche Anzahl von Bytes erzeugen
C
Modus und genau die gleiche Anzahl von BytesC++
Modus. Ist Ihnen ein Übertragungsfehler unterlaufen?– RonJohn
22. Juni 2019 um 0:13 Uhr