Question

Tout d'abord, je suis pas à la recherche d'un moyen de forcer le compilateur à inline la mise en œuvre de toutes les fonctions.

Pour réduire le niveau des réponses erronées assurez-vous de comprendre ce que le mot-clé inline en fait des moyens. Voici une bonne description, en ligne vs vs statique extern.

Alors, ma question, pourquoi ne pas marquer toutes les définitions fonction inline? Idéalement-à-dire, la seule unité de compilation serait main.cpp. Ou peut-être un peu plus pour les fonctions qui ne peuvent pas être définies dans un fichier d'en-tête (idiome Pimpl, etc).

La théorie derrière cette étrange requête est-il donnerait l'information maximale optimiseur pour travailler avec. Il pourrait inline implémentations de fonction bien sûr, mais il pourrait aussi faire l'optimisation « cross-module » car il n'y a qu'un seul module. Y at-il d'autres avantages?

At-on essayé avec une application réelle? Est-ce que l'augmentation de la performance? diminuer?!?

Quels sont les inconvénients de marquer toutes les définitions de fonction inline?

  • Compilation pourrait être plus lent et consomme beaucoup plus de mémoire.
  • itératives construit sont brisés, l'application entière devra être reconstruit après chaque changement.
  • Lien fois peut-être astronomique

Tous ces inconvénients ne sont actifs le développeur. Quels sont les inconvénients d'exécution?

Était-ce utile?

La solution

vous avez tout de #include signifie vraiment? Cela vous donne un seul module et laisser l'optimiseur voir l'ensemble du programme à la fois.

En fait, Visual C ++ de Microsoft fait exactement quand vous utilisez le /GL (Programme entier commutateur d'optimisation) , il ne fait rien jusqu'à ce que la compilation des pistes de liaison et a accès à tous les codes. D'autres compilateurs ont des options similaires.

Autres conseils

SQLite utilise cette idée. Au cours du développement, il utilise une structure de source traditionnelle. Mais pour une utilisation réelle il y a un énorme fichier c (112k lignes). Ils le font pour une optimisation maximale. Environ 5-10% la revendication amélioration de la performance

http://www.sqlite.org/amalgamation.html

(et d'autres sociétés de jeux) a essayé via la fabrication d'une uber-.CPP que #includeed tous les autres; c'est une technique connue. Dans notre cas, il ne semble pas affecter l'exécution beaucoup, mais les inconvénients de compilation que vous mentionnez se sont avérés être tout à fait rédhibitoire. Avec une demi-heure après la compilation de chaque changement unique, il devient impossible de itérer efficacement. (Et c'est avec l'application divvied vers le haut dans plus d'une douzaine différentes bibliothèques.)

Nous avons essayé de faire une configuration différente telle que nous aurions plusieurs .OBJs pendant le débogage et alors le seul uber-RPC dans la version opt-construit, mais a couru dans le problème du compilateur simplement à court de mémoire. Pour une application suffisamment grand, les outils sont tout simplement pas à la compilation d'un fichier cpp ligne de plusieurs millions.

Nous avons essayé LTCG aussi bien, et qui a fourni un petit mais bon coup de pouce d'exécution, dans les rares cas où il ne tombe pas en panne simplement pendant la phase de liaison.

Question intéressante! Vous avez certainement raison que tous les inconvénients énumérés sont spécifiques au développeur. Je dirais, cependant, qu'un développeur est désavantagé beaucoup moins susceptible de produire un produit de qualité. Il peut y avoir aucun inconvénient d'exécution, mais imaginez combien réticente développeur sera de faire de petits changements si chaque compilation prend des heures (ou jours même) pour compléter.

Je regarde cela d'un angle « d'optimisation prématurée »: le code modulaire dans plusieurs fichiers rend la vie plus facile pour le programmeur, donc il y a un avantage évident pour faire les choses de cette façon. Seulement si une application spécifique se révèle courir trop lent, et il peut être démontré que tout inline fait une amélioration mesurée, même que je considérer incommoder les développeurs. Même alors, il serait après la majorité du développement a été fait (pour qu'il puisse être mesuré) et ne serait probablement fait pour la production construit.

Ceci est semi-liée, mais notez que Visual C ++ a la capacité de faire l'optimisation inter-module, y compris en ligne à travers des modules. Voir http://msdn.microsoft.com/en- nous / bibliothèque / 0zza0de8% 28VS.80% 29.aspx pour plus d'informations.

Pour ajouter une réponse à votre question initiale, je ne pense pas à l'exécution il y aurait un inconvénient, en supposant l'optimiseur a été assez intelligent (donc pourquoi il a été ajouté comme option d'optimisation dans Visual Studio). Il suffit d'utiliser un compilateur assez intelligent pour le faire automatiquement, sans créer tous les problèmes que vous mentionnez. :)

Petit avantage Sur un bon compilateur pour une plate-forme moderne, inline ne touchera que très peu de fonctions. Il est juste un indice au compilateur, les compilateurs modernes sont assez bons à se rendre cette décision, et les frais généraux d'un appel de fonction est devenue assez faible (souvent, le principal avantage de inline est de ne pas réduire les frais généraux d'appel, mais l'ouverture des optimisations supplémentaires).

compilation Toutefois, étant donné en ligne modifie également la sémantique, vous devrez #include tout en une seule unité énorme compilation. Cette habituellement augmente le temps de compilation de manière significative, ce qui est un tueur sur les grands projets.

Code de taille si vous vous éloignez des plates-formes de bureau actuelles et ses compilateurs de haute performance, les choses changent beaucoup. Dans ce cas, la taille du code augmenté généré par un compilateur moins intelligent sera un problème - tant que cela rend le code beaucoup plus lent. Sur les plateformes embarquées, la taille du code est généralement la première restriction.

Pourtant, certains projets peuvent et tirer profit de « tout en ligne ». Il vous donne le même effet que l'optimisation des temps de liaison, au moins si votre compilateur ne suit pas aveuglément le inline.

Il est déjà fait dans certains cas. Il est très similaire à l'idée de l'unité construit , et les avantages et les inconvénients ne sont pas fa de ce que vous descibe:

  • plus de potentiel pour le compilateur à optimiser
  • temps de lien est essentiellement loin (si tout est en une seule unité de traduction, il n'y a rien à lien, vraiment)
  • compilation va bien, d'une façon ou l'autre. Incrémental builds deviennent impossibles, comme vous l'avez mentionné. D'autre part, une construction complète va être plus rapide que ce serait autrement (comme chaque ligne de code est compilé exactement une fois. Dans une construction régulière, le code des en-têtes finit par être compilé dans chaque unité de traduction où l'en-tête est inclus )

Mais dans les cas où vous avez déjà beaucoup de code d'en-tête uniquement (par exemple si vous utilisez beaucoup de Boost), il pourrait être une optimisation très utile, tant en termes de temps de construction et de la performance exécutable.

Comme toujours cependant, lorsque la performance est impliqué, cela dépend. Ce n'est pas une mauvaise idée, mais ce n'est pas universellement applicable soit.

En ce qui concerne le temps buld va, vous avez deux façons de l'optimiser:

  • réduire le nombre d'unités de traduction (donc vos en-têtes sont inclus dans moins d'endroits), ou
  • minimiser la quantité de code dans les en-têtes (de sorte que le coût d'inclure un en-tête dans plusieurs unités de traduction diminue)

le code C prend généralement la deuxième option, à peu près à l'extrême: presque rien en dehors des déclarations avant et les macros sont conservés dans les en-têtes. C ++ souvent des mensonges autour du milieu, qui est l'endroit où vous obtenez le pire moment de la construction totale possible (mais PCH et / ou construit progressivement peut raser un peu de temps à nouveau hors), mais d'aller plus loin dans l'autre sens, ce qui réduit le nombre d'unités de traduction peuvent vraiment faire des merveilles pour le temps total de construction.

C'est à peu près la philosophie derrière entier Optimisation Programme et lien temps de génération de code (LTCG):. possibilités d'optimisation sont les meilleures avec une connaissance globale

D'un point de vue pratique, il est en quelque sorte d'une douleur parce que maintenant tous les changements que vous prenez, nécessitera une recompilation de votre arbre des sources. D'une manière générale, vous avez besoin d'une version optimisée moins souvent que vous devez apporter des modifications arbitraires.

J'ai essayé à l'époque Metrowerks (il est assez facile à installer avec une construction de style « unité ») et la compilation n'a jamais fini. Je le mentionne seulement pour souligner que c'est une configuration de flux de travail qui est susceptible de taxer l'ensemble des outils de manière qu'ils n'anticipaient.

L'hypothèse est que le compilateur ne peut pas optimiser l'ensemble des fonctions. C'est une limitation des compilateurs spécifiques et non pas un problème général. En utilisant cela comme une solution générale pour un problème spécifique pourrait être mauvais. Le compilateur peut très bien que votre programme avec ballonnement ce qui aurait pu être des fonctions réutilisables à la même adresse mémoire (Parvenir à l'utilisation du cache) étant compilé ailleurs (et perte de performance en raison du cache).

Big fonctions dans le coût général sur l'optimisation, il y a un équilibre entre les frais généraux des variables locales et la quantité de code dans la fonction. Maintenir le nombre de variables dans la fonction (à la fois passé dans, local et global) à l'intérieur du nombre de variables à usage unique pour les résultats de la plate-forme dans la plupart de tout pouvoir rester dans les registres et pas à être évincé de RAM, également une pile cadre n'est pas nécessaire (dépend de la cible) afin d'appeler la fonction tête est sensiblement réduite. Difficile à faire dans les applications du monde réel tout le temps, mais l'alternative d'un petit nombre de grandes fonctions avec beaucoup de variables locales le code va passer beaucoup de registres expulsant et temps de chargement avec des variables à / de RAM (dépend de la cible).

Essayez LLVM il permet d'optimiser l'ensemble du programme non seulement fonction par fonction. Le 27 avait pris à l'optimiseur de gcc, au moins pour un test ou deux, je na pas faire des tests de performance exhaustive. Et 28 est sorti donc je suppose qu'il vaut mieux. Même avec quelques fichiers le nombre de combinaisons de réglage sont trop nombreux pour jouer avec. Je trouve préférable de ne pas optimiser du tout jusqu'à ce que vous avez tout le programme dans un fichier, puis effectuez votre optimisation, ce qui donne l'optimiseur tout le programme de travail avec, au fond ce que vous essayez de faire avec inline, mais sans les bagages.

On suppose foo() et bar() appellent tous deux un peu helper(). Si tout est dans une unité de compilation, le compilateur peut choisir de ne pas inline helper(), afin de réduire la taille de l'instruction totale. Cela provoque foo() de faire un appel de fonction non inline à helper().

Le compilateur ne sait pas qu'une amélioration de la nanoseconde à la durée de foo() ajoute $ 100 / jour à votre ligne de fond dans l'attente. Il ne sait pas qu'une amélioration de la performance ou de la dégradation de rien en dehors de foo() n'a pas d'impact sur vos résultats.

Seulement vous en tant que programmeur savent ces choses (après le profilage et l'analyse minutieuse des cours). La décision de ne pas bar() en ligne est une façon de dire au compilateur ce que vous savez.

Le problème avec inline est que vous voulez que les fonctions de haute performance pour tenir dans le cache. Vous pourriez penser que les frais généraux fonction d'appel est le grand succès de la performance, mais dans de nombreuses architectures de cache manquant soufflera le couple pousse et pops hors de l'eau. Par exemple, si vous avez une grande fonction (peut-être profonde) qui doit être appelé très rarement de votre chemin haute performance principale, il pourrait causer votre boucle principale de haute performance de croître au point où il ne rentre pas dans L1 Icache. Cela va ralentir votre code vers le bas chemin, beaucoup plus que l'appel de fonction occasionnelle.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top