Lorsque vous utilisez le même code, changer simplement le compilateur (d'un compilateur C à un compilateur C ++) changera la quantité de mémoire allouée. Je ne sais pas trop pourquoi et j'aimerais mieux comprendre. Jusqu'à présent, la meilleure réponse que j'ai obtenue est "probablement les flux d'E / S", ce qui n'est pas très descriptif et me fait m'interroger sur l'aspect "vous ne payez pas pour ce que vous n'utilisez pas" du C ++.
J'utilise les compilateurs Clang et GCC, versions 7.0.1-8 et 8.3.0-6 respectivement. Mon système fonctionne sur Debian 10 (Buster), le dernier. Les benchmarks se font via Valgrind Massif.
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
Le code utilisé ne change pas, mais que je compile en C ou en C ++, cela change les résultats du benchmark Valgrind. Les valeurs restent cependant cohérentes entre les compilateurs. Les allocations d'exécution (pic) pour le programme se présentent comme suit:
- GCC (C): 1 032 octets (1 Ko)
- G ++ (C ++): 73 744 octets, (~ 74 Ko)
- Clang (C): 1 032 octets (1 Ko)
- Clang ++ (C ++): 73 744 octets (~ 74 Ko)
Pour la compilation, j'utilise les commandes suivantes:
clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp
Pour Valgrind, je tourne valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-lang
sur chaque compilateur et langage, puis ms_print
pour afficher les pics.
Est-ce que je fais quelque chose de mal ici?
la source
try
bloc au détriment d'une plus grande empreinte mémoire, peut-être avec une table de saut ou quelque chose. Essayez peut-être de compiler sans exceptions et voyez quel impact cela a. Edit: En fait, essayez de désactiver de manière itérative diverses fonctionnalités c ++ pour voir quel impact cela a sur l'empreinte mémoire.clang++ -xc
au lieu declang
, la même allocation était là, ce qui suggère fortement que cela est dû aux bibliothèques liéesC
mode et le même nombre exact d'octets enC++
mode. Avez-vous fait une erreur de transcription?Réponses:
L'utilisation du tas provient de la bibliothèque standard C ++. Il alloue de la mémoire pour une utilisation interne de la bibliothèque au démarrage. Si vous ne le liez pas, il ne devrait y avoir aucune différence entre la version C et C ++. Avec GCC et Clang, vous pouvez compiler le fichier avec:
Cela demandera à l'éditeur de liens de ne pas établir de lien avec des bibliothèques inutilisées. Dans votre exemple de code, la bibliothèque C ++ n'est pas utilisée, elle ne doit donc pas être liée à la bibliothèque standard C ++.
Vous pouvez également tester cela avec le fichier C. Si vous compilez avec:
L'utilisation du tas réapparaîtra, même si vous avez construit un programme C.
L'utilisation du tas dépend évidemment de l'implémentation de bibliothèque C ++ spécifique que vous utilisez. Dans votre cas, c'est la bibliothèque GNU C ++, libstdc ++ . D'autres implémentations peuvent ne pas allouer la même quantité de mémoire, ou ne pas allouer de mémoire du tout (du moins pas au démarrage.) La bibliothèque LLVM C ++ ( libc ++ ) par exemple ne fait pas d'allocation de tas au démarrage, du moins sur mon Linux machine:
L'utilisation du tas est la même que de ne pas du tout lier contre lui.
(Si la compilation échoue, alors libc ++ n'est probablement pas installée. Le nom du paquet contient généralement "libc ++" ou "libcxx".)
la source
-Wl,--as-needed
indicateur supprime les bibliothèques que vous spécifiez dans vos-l
indicateurs mais que vous n'utilisez pas réellement. Donc, si vous n'utilisez pas de bibliothèque, alors ne liez pas contre elle. Vous n'avez pas besoin de ce drapeau pour cela. Cependant, si votre système de construction ajoute trop de bibliothèques et qu'il faudrait beaucoup de travail pour toutes les nettoyer et ne lier que celles nécessaires, vous pouvez utiliser cet indicateur à la place. La bibliothèque standard est cependant une exception, car elle est automatiquement liée à. C'est donc une affaire de coin.Ni GCC ni Clang ne sont des compilateurs - ce sont en fait des programmes de pilote de chaîne d'outils. Cela signifie qu'ils invoquent le compilateur, l'assembleur et l'éditeur de liens.
Si vous compilez votre code avec un compilateur C ou C ++, vous obtiendrez le même assembly produit. L'assembleur produira les mêmes objets. La différence est que le pilote de la chaîne d'outils fournira une entrée différente à l'éditeur de liens pour les deux langues différentes: des démarrages différents (C ++ nécessite du code pour exécuter les constructeurs et les destructeurs pour les objets avec une durée de stockage statique ou locale au niveau de l'espace de noms, et nécessite une infrastructure pour la pile frames pour prendre en charge le déroulement pendant le traitement des exceptions, par exemple), la bibliothèque standard C ++ (qui a également des objets de durée de stockage statique au niveau de l'espace de noms), et probablement des bibliothèques d'exécution supplémentaires (par exemple, libgcc avec son infrastructure de déroulement de pile).
En bref, ce n'est pas le compilateur qui cause l'augmentation de l'encombrement, c'est la liaison des éléments que vous avez choisi d'utiliser en choisissant le langage C ++.
Il est vrai que C ++ a la philosophie «ne payez que pour ce que vous utilisez», mais en utilisant le langage, vous le payez. Vous pouvez désactiver certaines parties du langage (RTTI, gestion des exceptions) mais vous n'utilisez plus C ++. Comme mentionné dans une autre réponse, si vous n'utilisez pas du tout la bibliothèque standard, vous pouvez demander au pilote de la laisser de côté (--Wl, - selon les besoins) mais si vous n'utilisez aucune des fonctionnalités de C ++ ou de sa bibliothèque, pourquoi choisissez-vous même C ++ comme langage de programmation?
la source