La compilation d'un fichier C ++ prend beaucoup de temps par rapport à C # et Java. La compilation d'un fichier C ++ prend beaucoup plus de temps que l'exécution d'un script Python de taille normale. J'utilise actuellement VC ++ mais c'est la même chose avec n'importe quel compilateur. Pourquoi est-ce?
Les deux raisons auxquelles je pouvais penser étaient le chargement des fichiers d'en-tête et l'exécution du préprocesseur, mais cela ne semble pas expliquer pourquoi cela prend autant de temps.
c++
performance
compilation
Dan Goldstein
la source
la source
It takes significantly longer to compile a C++ file
- tu veux dire 2 secondes contre 1 seconde? C'est certainement deux fois plus long, mais peu significatif. Ou voulez-vous dire 10 minutes contre 5 secondes? Veuillez quantifier.Réponses:
Plusieurs raisons
Fichiers d'en-tête
Chaque unité de compilation nécessite des centaines, voire des milliers d'en-têtes à (1) charger et (2) compiler. Chacun d'entre eux doit généralement être recompilé pour chaque unité de compilation, car le préprocesseur garantit que le résultat de la compilation d'un en-tête peut varier entre chaque unité de compilation. (Une macro peut être définie dans une unité de compilation qui modifie le contenu de l'en-tête).
C'est probablement la raison principale, car cela nécessite de grandes quantités de code à compiler pour chaque unité de compilation, et en outre, chaque en-tête doit être compilé plusieurs fois (une fois pour chaque unité de compilation qui l'inclut).
Mise en relation
Une fois compilés, tous les fichiers objets doivent être liés ensemble. Il s'agit essentiellement d'un processus monolithique qui ne peut pas très bien être parallélisé et doit traiter l'ensemble de votre projet.
Analyse
La syntaxe est extrêmement compliquée à analyser, dépend fortement du contexte et est très difficile à lever. Cela prend beaucoup de temps.
Modèles
En C #,
List<T>
est le seul type compilé, quel que soit le nombre d'instanciations de List que vous avez dans votre programme. En C ++,vector<int>
est un type complètement distinct devector<float>
, et chacun devra être compilé séparément.Ajoutez à cela que les modèles constituent un «sous-langage» complet de Turing complet que le compilateur doit interpréter, et cela peut devenir ridiculement compliqué. Même un code de métaprogrammation de modèle relativement simple peut définir des modèles récursifs qui créent des dizaines et des dizaines d'instanciations de modèle. Les modèles peuvent également entraîner des types extrêmement complexes, avec des noms ridiculement longs, ce qui ajoute beaucoup de travail supplémentaire à l'éditeur de liens. (Il faut comparer beaucoup de noms de symboles, et si ces noms peuvent devenir des milliers de caractères, cela peut devenir assez cher).
Et bien sûr, ils aggravent les problèmes avec les fichiers d'en-tête, car les modèles doivent généralement être définis dans les en-têtes, ce qui signifie que beaucoup plus de code doit être analysé et compilé pour chaque unité de compilation. En code C ordinaire, un en-tête ne contient généralement que des déclarations avancées, mais très peu de code réel. En C ++, il n'est pas rare que presque tout le code réside dans des fichiers d'en-tête.
Optimisation
C ++ permet des optimisations très spectaculaires. C # ou Java ne permettent pas d'éliminer complètement les classes (elles doivent être là à des fins de réflexion), mais même un simple métaprogramme de modèle C ++ peut facilement générer des dizaines ou des centaines de classes, qui sont toutes intégrées et éliminées à nouveau dans l'optimisation phase.
De plus, un programme C ++ doit être entièrement optimisé par le compilateur. Le programme AC # peut s'appuyer sur le compilateur JIT pour effectuer des optimisations supplémentaires au moment du chargement, C ++ n'obtient pas de telles "secondes chances". Ce que le compilateur génère est aussi optimisé que possible.
Machine
C ++ est compilé en code machine qui peut être un peu plus compliqué que l'utilisation de Java ou .NET en bytecode (en particulier dans le cas de x86). (Ceci n'est mentionné par souci d'exhaustivité que parce qu'il a été mentionné dans les commentaires et autres. En pratique, cette étape ne prendra probablement pas plus qu'une infime fraction du temps de compilation total).
Conclusion
La plupart de ces facteurs sont partagés par le code C, qui se compile assez efficacement. L'étape d'analyse est beaucoup plus compliquée en C ++ et peut prendre beaucoup plus de temps, mais le principal délinquant est probablement les modèles. Ils sont utiles et font de C ++ un langage beaucoup plus puissant, mais ils pèsent également sur la vitesse de compilation.
la source
Le ralentissement n'est pas nécessairement le même avec n'importe quel compilateur.
Je n'ai pas utilisé Delphi ou Kylix mais à l'époque MS-DOS, un programme Turbo Pascal se compilait presque instantanément, tandis que le programme Turbo C ++ équivalent ne faisait qu'explorer.
Les deux principales différences étaient un système de modules très solide et une syntaxe qui permettait la compilation en un seul passage.
Il est certainement possible que la vitesse de compilation n'ait tout simplement pas été une priorité pour les développeurs de compilateurs C ++, mais il existe également des complications inhérentes à la syntaxe C / C ++ qui la rendent plus difficile à traiter. (Je ne suis pas un expert en C, mais Walter Bright l'est, et après avoir construit divers compilateurs commerciaux C / C ++, il a créé le langage D. L'une de ses modifications a consisté à appliquer une grammaire sans contexte pour rendre le langage plus facile à analyser. .)
De plus, vous remarquerez que généralement les Makefiles sont configurés de sorte que chaque fichier soit compilé séparément en C, donc si 10 fichiers source utilisent tous le même fichier include, ce fichier inclus est traité 10 fois.
la source
L'analyse et la génération de code sont en fait assez rapides. Le vrai problème est l'ouverture et la fermeture de fichiers. N'oubliez pas que même avec les gardes d'inclusion, le compilateur a toujours ouvert le fichier .H et lu chaque ligne (puis l'ignore).
Un ami une fois (alors qu'il s'ennuyait au travail), a pris la demande de son entreprise et a mis tout - tous les fichiers source et en-tête - dans un gros fichier. Le temps de compilation est passé de 3 heures à 7 minutes.
la source
Une autre raison est l'utilisation du pré-processeur C pour localiser les déclarations. Même avec des protège-têtes, .h doit encore être analysé encore et encore, chaque fois qu'ils sont inclus. Certains compilateurs prennent en charge les en-têtes précompilés qui peuvent vous aider, mais ils ne sont pas toujours utilisés.
Voir aussi: Réponses C ++ fréquemment posées
la source
C ++ est compilé en code machine. Vous avez donc le préprocesseur, le compilateur, l'optimiseur et enfin l'assembleur, qui doivent tous fonctionner.
Java et C # sont compilés en octet-code / IL, et la machine virtuelle Java / .NET Framework s'exécutent (ou compilent JIT en code machine) avant l'exécution.
Python est un langage interprété qui est également compilé en code octet.
Je suis sûr qu'il y a aussi d'autres raisons à cela, mais en général, ne pas avoir à compiler en langage machine natif fait gagner du temps.
la source
Les principaux problèmes sont les suivants:
1) L'analyse infinie de l'en-tête. Déjà mentionné. Les atténuations (comme #pragma une fois) ne fonctionnent généralement que par unité de compilation, pas par build.
2) Le fait que la chaîne d'outils est souvent séparée en plusieurs fichiers binaires (make, préprocesseur, compilateur, assembleur, archiveur, impdef, linker et dlltool dans les cas extrêmes) qui doivent tous réinitialiser et recharger tous les états tout le temps pour chaque appel ( compilateur, assembleur) ou tous les deux fichiers (archiveur, éditeur de liens et dlltool).
Voir aussi cette discussion sur comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078 spécialement celle-ci:
http://compilers.iecc.com/comparch/article/02-07-128
Notez que John, le modérateur de comp.compilers semble être d'accord, et que cela signifie qu'il devrait être possible d'atteindre des vitesses similaires pour C aussi, si l'on intègre pleinement la chaîne d'outils et implémente des en-têtes précompilés. De nombreux compilateurs C commerciaux le font dans une certaine mesure.
Notez que le modèle Unix de factorisation de tout dans un binaire séparé est une sorte de pire modèle pour Windows (avec sa création de processus lente). Il est très visible lorsque l'on compare les temps de construction de GCC entre Windows et * nix, surtout si le système make / configure appelle également certains programmes juste pour obtenir des informations.
la source
Construire C / C ++: ce qui se passe vraiment et pourquoi cela prend-il autant de temps
Une partie relativement importante du temps de développement logiciel n'est pas consacrée à l'écriture, à l'exécution, au débogage ou même à la conception de code, mais à l'attente de la fin de la compilation. Pour accélérer les choses, nous devons d'abord comprendre ce qui se passe lorsque le logiciel C / C ++ est compilé. Les étapes sont à peu près les suivantes:
Nous allons maintenant examiner chaque étape plus en détail en nous concentrant sur la façon dont elles peuvent être accélérées.
Configuration
Il s'agit de la première étape lors du démarrage de la construction. Signifie généralement l'exécution d'un script de configuration ou CMake, Gyp, SCons ou un autre outil. Cela peut prendre entre une seconde et plusieurs minutes pour les très gros scripts de configuration basés sur Autotools.
Cette étape se produit relativement rarement. Il doit uniquement être exécuté lors de la modification des configurations ou de la modification de la configuration de build. À moins de changer les systèmes de construction, il n'y a pas grand-chose à faire pour accélérer cette étape.
Création d'un outil de démarrage
C'est ce qui se produit lorsque vous exécutez make ou cliquez sur l'icône de génération sur un IDE (qui est généralement un alias pour make). L'outil de construction binaire démarre et lit ses fichiers de configuration ainsi que la configuration de construction, qui sont généralement la même chose.
Selon la complexité et la taille de la construction, cela peut prendre de une fraction de seconde à plusieurs secondes. En soi, ce ne serait pas si mal. Malheureusement, la plupart des systèmes de build basés sur la marque font invoquer make à des dizaines à des centaines de fois pour chaque build. Habituellement, cela est dû à une utilisation récursive de make (ce qui est mauvais).
Il convient de noter que la raison pour laquelle Make est si lent n'est pas un bogue d'implémentation. La syntaxe de Makefiles a quelques bizarreries qui rendent une implémentation très rapide presque impossible. Ce problème est encore plus visible lorsqu'il est combiné avec l'étape suivante.
Vérification des dépendances
Une fois que l'outil de construction a lu sa configuration, il doit déterminer quels fichiers ont changé et lesquels doivent être recompilés. Les fichiers de configuration contiennent un graphique acyclique dirigé décrivant les dépendances de construction. Ce graphique est généralement créé lors de l'étape de configuration. Le temps de démarrage de l'outil de génération et le scanner de dépendances sont exécutés sur chaque génération. Leur runtime combiné détermine la borne inférieure du cycle d'édition-compilation-débogage. Pour les petits projets, cette durée est généralement de quelques secondes environ. C'est tolérable. Il existe des alternatives à Make. Le plus rapide d'entre eux est Ninja, qui a été construit par les ingénieurs de Google pour Chromium. Si vous utilisez CMake ou Gyp pour construire, passez simplement à leurs backends Ninja. Vous n'avez rien à changer dans les fichiers de construction eux-mêmes, profitez simplement de l'augmentation de vitesse. Ninja n'est pas inclus dans la plupart des distributions, cependant,
Compilation
À ce stade, nous invoquons enfin le compilateur. En coupant quelques coins, voici les étapes approximatives prises.
Contrairement à la croyance populaire, la compilation de C ++ n'est pas si lente. La STL est lente et la plupart des outils de compilation utilisés pour compiler C ++ sont lents. Cependant, il existe des outils et des moyens plus rapides pour atténuer les parties lentes du langage.
Leur utilisation prend un peu d'huile de coude, mais les avantages sont indéniables. Des temps de construction plus rapides conduisent à des développeurs plus heureux, plus d'agilité et, finalement, un meilleur code.
la source
Un langage compilé va toujours nécessiter une surcharge initiale plus importante qu'un langage interprété. De plus, vous n'avez peut-être pas très bien structuré votre code C ++. Par exemple:
Compile beaucoup plus lentement que:
la source
Un moyen simple de réduire le temps de compilation dans les projets C ++ plus volumineux consiste à créer un fichier d'inclusion * .cpp qui inclut tous les fichiers cpp de votre projet et à le compiler. Cela réduit le problème d'explosion d'en-tête à une seule fois. L'avantage est que les erreurs de compilation feront toujours référence au fichier correct.
Par exemple, supposons que vous ayez a.cpp, b.cpp et c.cpp .. créez un fichier: everything.cpp:
Ensuite, compilez le projet en créant simplement everything.cpp
la source
Quelques raisons:
1) La grammaire C ++ est plus complexe que C # ou Java et prend plus de temps à analyser.
2) (Plus important) Le compilateur C ++ produit du code machine et fait toutes les optimisations pendant la compilation. C # et Java vont à mi-chemin et laissent ces étapes à JIT.
la source
Le compromis que vous obtenez est que le programme s'exécute un peu plus vite. Cela peut être un réconfort pour vous pendant le développement, mais cela pourrait être très important une fois le développement terminé et le programme est simplement exécuté par les utilisateurs.
la source
La plupart des réponses ne sont pas claires en mentionnant que C # s'exécutera toujours plus lentement en raison du coût des actions qui, en C ++, ne sont effectuées qu'une seule fois au moment de la compilation, ce coût de performance est également impacté en raison des dépendances d'exécution (plus de choses à charger pour pouvoir pour s'exécuter), sans oublier que les programmes C # auront toujours une empreinte mémoire plus élevée, ce qui se traduira par des performances plus étroitement liées à la capacité du matériel disponible. Il en va de même pour les autres langues interprétées ou dépendantes d'une machine virtuelle.
la source
Il y a deux problèmes auxquels je peux penser qui pourraient affecter la vitesse de compilation de vos programmes en C ++.
PROBLÈME POSSIBLE # 1 - COMPILATION DE L'EN-TÊTE: (Cela peut ou peut ne pas avoir déjà été résolu par une autre réponse ou un autre commentaire.) Microsoft Visual C ++ (AKA VC ++) prend en charge les en-têtes précompilés, ce que je recommande fortement. Lorsque vous créez un nouveau projet et sélectionnez le type de programme que vous créez, une fenêtre d'assistant de configuration doit apparaître sur votre écran. Si vous appuyez sur le bouton "Suivant>" au bas de celui-ci, la fenêtre vous amènera à une page qui a plusieurs listes de fonctionnalités; assurez-vous que la case à côté de l'option «En-tête précompilé» est cochée. (REMARQUE: c'est mon expérience avec les applications de console Win32 en C ++, mais cela peut ne pas être le cas avec toutes sortes de programmes en C ++.)
PROBLÈME POSSIBLE # 2 - L'EMPLACEMENT ÉTÉ COMPILÉ POUR: Cet été, j'ai suivi un cours de programmation, et nous avons dû stocker tous nos projets sur des lecteurs flash de 8 Go, car les ordinateurs du laboratoire que nous utilisions étaient effacés tous les soirs à minuit, qui aurait effacé tout notre travail. Si vous compilez vers un périphérique de stockage externe pour des raisons de portabilité / sécurité / etc., cela peut prendre très longtempstemps (même avec les en-têtes précompilés que j'ai mentionnés ci-dessus) pour que votre programme compile, surtout s'il s'agit d'un programme assez volumineux. Mon conseil pour vous dans ce cas serait de créer et de compiler des programmes sur le disque dur de l'ordinateur que vous utilisez, et chaque fois que vous voulez / devez arrêter de travailler sur vos projets pour quelque raison que ce soit, transférez-les sur votre externe périphérique de stockage, puis cliquez sur l'icône «Retirer le matériel en toute sécurité et éjecter le support», qui devrait apparaître comme un petit lecteur flash derrière un petit cercle vert avec une coche blanche dessus, pour le déconnecter.
J'espère que ceci vous aide; faites-moi savoir si c'est le cas! :)
la source