D'après ce que j'ai lu, le compilateur n'est pas obligé de remplacer l'appel de fonction d'une fonction en ligne par son corps, mais le fera s'il le peut. Cela m'a fait réfléchir - pourquoi avons-nous le mot en ligne si c'est le cas? Pourquoi ne pas faire toutes les fonctions de fonction en ligne par défaut et laisser le compilateur déterminer s'il peut ou non remplacer les appels par le corps de la fonction?
c++
language-design
optimization
compilation
EpsilonVector
la source
la source
Réponses:
inline
est de C; ce n'était pas nouveau pour C ++.Il existe des mots clés C (
register
etinline
) qui ont été conçus pour permettre au programmeur d'aider à l'optimisation du code. Ceux-ci sont généralement ignorés de nos jours, car les compilateurs peuvent faire mieux pour l'affectation des registres et pour décider quand incorporer des fonctions (en fait, un compilateur peut incorporer ou non une fonction à des moments différents). La génération de code sur les processeurs modernes est beaucoup plus compliquée que sur les plus déterministes courantes lorsque Ritchie inventait C.Ce que le mot signifie maintenant, en C ++, c'est qu'il peut avoir plusieurs définitions identiques et doit être défini dans chaque unité de traduction qui l'utilise. (En d'autres termes, vous devez vous assurer qu'elle peut être insérée.) Vous pouvez avoir une
inline
fonction dans un en-tête sans problème, et les fonctions membres définies dans une définition de classe sont automatiquement efficacesinline
.la source
inline
.inline
été normalisé en C ++ en premier, bien qu'il soit déjà disponible en tant qu'extension de fournisseur en C .... oui, il semble qu'il ait été ajouté à la norme C en C99.inline
C99 et ultérieur sont différentes des règles deinline
C ++.Initialement, il y
inline
avait un indice très fort que les appels à la fonction devaient être intégrés.Mais le seul effet garanti de
inline
est de permettre à une fonction d'être définie (à l'identique) dans plusieurs unités de traduction, par exemple, que vous placez la définition dans un fichier d'en-tête.De nos jours, certains compilateurs sont très désireux de suivre le conseil en ligne, par exemple g ++. Et certains compilateurs le prennent moins au sérieux, par exemple Visual C ++. Mais tous doivent respecter la garantie.
Il est regrettable que ces deux significations - indice d'optimisation et ce que nous pourrions appeler une définition jetable au niveau de l'éditeur de liens - résident avec le même mot clé, car cela signifie que vous ne pouvez pratiquement pas en avoir un sans l'autre.
Il est également regrettable que
inline
(ou mieux, un mot-clé distinct sur la définition jetable) ne puisse pas être appliqué aux données .Le besoin de données jetables au niveau de l'éditeur de liens a augmenté à mesure que les modules uniquement en-tête sont devenus plus populaires. Par exemple, de nombreuses sous-bibliothèques Boost sont uniquement en-tête.
Pour les données, vous pouvez cependant appliquer une petite astuce avec les modèles. Définissez-le dans un modèle de classe, fournissez un
typedef
paramètre de modèlevoid
(ou autre). En effet, la règle de définition unique fait une exception spécifique pour les modèles.Remarques:
¹ les
inline
variables seront prises en charge en C ++ 17 .la source
inline
.Pourquoi ne pas rendre toutes les fonctions en ligne par défaut? Parce que c'est un compromis d'ingénierie. Il existe au moins deux types d '"optimisation": accélérer le programme et réduire la taille (empreinte mémoire) du programme. L'intégration accélère généralement les choses. Il supprime la surcharge de l'appel de fonction, évitant de pousser puis d'extraire des paramètres de la pile. Cependant, cela augmente également l'empreinte mémoire du programme, car chaque appel de fonction doit maintenant être remplacé par le code complet de la fonction. Pour rendre les choses encore plus compliquées, n'oubliez pas que le CPU stocke des morceaux de mémoire fréquemment utilisés dans un cache sur le CPU pour un accès ultra-rapide. Si vous agrandissez suffisamment l'image mémoire du programme, votre programme ne pourra pas utiliser efficacement le cache et, dans le pire des cas, l'inlining pourrait en fait ralentir votre programme.
la source
Pour comprendre «en ligne», vous devez comprendre l'histoire et à quoi ressemblait la vie il y a 20 (et 30) ans.
Nous écrivions du code sur des ordinateurs qui avaient peu de mémoire, il n'était donc pas possible pour un compilateur de traiter tout le code qui composait un programme d'un seul coup. Le compilateur était également très lent, donc vous ne vouliez pas avoir à recompiler le code qui n'avait pas changé - prendre plus de 24 heures (sur un ordinateur qui coûte plus cher qu'une voiture haut de gamme) pour recompiler tout le code était normal pour quelques projets que je travaillé.
Par conséquent, chaque fichier de code a été compilé séparément dans un fichier objet. Chaque fichier objet a commencé avec la liste de toutes les fonctions qu'il contenait, ainsi que «l'adresse» de la fonction. Un fichier objet avait également une liste de toutes les fonctions qu'il appelait dans d'autres fichiers objet ainsi que l'emplacement de l'appel.
Un éditeur de liens lirait d'abord tous les fichiers objets, et constituerait une liste de toutes les fonctions qu'ils ont exportées, ainsi que le fichier dans lequel ils se trouvaient et leur adresse. Il relirait ensuite tous les fichiers objets, les exportant dans le fichier programme, tout en mettant à jour tous les appels de fonction «externes» avec l'adresse de la fonction.
L'éditeur de liens n'a pas modifié ou optimisé le code machine produit par le compilateur autrement que pour corriger les références aux appels de fonction externes. L'éditeur de liens faisait partie du système d'exploitation et est antérieur à la plupart des compilateurs. Lorsque les gens ont écrit un nouveau compilateur, ils en avaient besoin pour fonctionner avec les éditeurs de liens actuels et pour pouvoir se lier aux fichiers d'objets actuels, sinon les appels système ne pourraient pas être effectués.
Le compilateur n'a vu le code que dans le fichier «.c» ou «.cpp» qu'il compilait avec tous les fichiers d'en-tête inclus. Il n'a donc pas pu effectuer d'optimisation basée sur du code dans d'autres fichiers «.c» ou «.cpp».
Le mot-clé «inline» a permis de définir le corps d'une fonction (méthode) dans un fichier d'en-tête, permettant ainsi au compilateur d'utiliser le code de la fonction lors de la compilation du code qui l'appelle. Par exemple, supposons que vous ayez défini une classe de collection dans un autre fichier .cpp, cette classe aurait une méthode «isEmpty», qui contenait une ligne de code, il y aurait une grande accélération du programme résultant si, au lieu d'un appel à une fonction , l'appel de fonction a été remplacé par cette seule ligne.
Le mot-clé «en ligne» était perçu à l'époque comme un moyen «bon marché et facile» de permettre l'encapsulation des données tout en évitant le coût des appels de fonction, sans quoi beaucoup de programmeurs n'auraient qu'à accéder aux champs privés de l'objet. (Macros où une façon bien pire d '«aligner» le code était courante à l'époque.)
De nos jours, les «linkers» font beaucoup d'optimisation de code et ont tendance à être écrits par l'équipe en tant que compilateur. Souvent, le compilateur vérifie simplement que le code est correct et le «compresse», laissant l'essentiel de la tâche de création de code machine à l'éditeur de liens.
la source
Voyons ce que dit la norme (surligné les parties importantes en gras):
Donc, si vous voulez en être sûr, vous devriez lire la documentation de votre compilateur.
Tout inclure est une mauvaise idée, car cela pourrait entraîner beaucoup de code machine en double ...
Vous devrez donc savoir:
la source
inline
en réitérant les connaissances car le PO semble manquer une partie, ce qui est la raison principale pour laquelle il ne comprend pas le point. Pour obtenir un point, il doit comprendre ce qui est en dessous de ce point ...Permettez-moi de vous donner une bonne raison d'utiliser le mot clé en ligne.
Sur un système intégré, tel qu'une imprimante de tickets ou un système plus petit similaire. Le processeur est très limité et un appel de fonction (préparation des paramètres de fonction sur la pile, appel, récupération des paramètres de la pile et remise de la réponse, etc.) peut prendre plusieurs ms à exécuter, à côté de la fonction elle-même.
Disons que le temps d'appel est d'environ 60 ms (juste pour appeler, pas la fonction réelle) et que vous avez fait 50 itérations (boucle ou appels itératifs dans une arborescence).
Le temps pour aller et venir de cet appel de fonction prendra 60 * 50 = 3000 (3 secondes).
Si vous avez la mémoire, vous feriez certainement une ligne pour économiser 3 secondes.
Donc, les inline sont essentiellement utilisés lorsque vous avez besoin d'une vitesse d'exécution. Dans certains projets auxquels j'ai participé, le temps d'appel était plus long que le temps d'exécution, une situation classique quand utiliser en ligne.
la source