Fonctions inline en C ++. À quoi ça sert?

19

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?

EpsilonVector
la source
1
Ici . Page 6
EpsilonVector

Réponses:

40

inlineest de C; ce n'était pas nouveau pour C ++.

Il existe des mots clés C ( registeret inline) 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 inlinefonction dans un en-tête sans problème, et les fonctions membres définies dans une définition de classe sont automatiquement efficaces inline.

David Thornley
la source
3
+1 pour l'histoire de inline.
Tamara Wijsman
11
Je suis à peu près sûr que cela a 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.
Ben Voigt
@Ben Voigt: Vous avez raison. Je l'ai rencontré pour la première fois en C.
David Thornley
5
Sachez également que non seulement l'historique est incorrect, mais que les règles de inlineC99 et ultérieur sont différentes des règles de inlineC ++.
Alf P. Steinbach
27

Initialement, il y inlineavait un indice très fort que les appels à la fonction devaient être intégrés.

Mais le seul effet garanti de inlineest 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 typedefparamètre de modèle void(ou autre). En effet, la règle de définition unique fait une exception spécifique pour les modèles.

Remarques:
¹ les inlinevariables seront prises en charge en C ++ 17 .

Alf P. Steinbach
la source
1
+1 pour les informations supplémentaires sur inline.
Tamara Wijsman
1
" effectivement à l'identique " est en fait une condition très délicate, car les langages C ++ très mal conçus essaient très fort de faire écrire aux programmeurs normalement compétents et prudents un code raisonnable mais formellement non défini qui utilise des objets statiques - je me demande combien de membres du comité tombent eux-mêmes dans le piège
curiousguy
6

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.

Charles E. Grant
la source
5
Mais le compilateur peut (et le fait!) Comprendre cela beaucoup mieux que le programmeur en général. Ce n'est donc pas un argument valable.
Konrad Rudolph
" chaque appel de fonction doit maintenant être remplacé par le code complet de la fonction " Et la fonction en ligne n'est pas une fonction où la séquence d'appel est omise et où le code assembleur de la fonction est copié en place. Une fonction en ligne est compilée en place, en mettant le code intermédiaire de haut niveau en ligne. Cela permet au compilateur de traiter efficacement le corps de la fonction comme une macro propre (une macro propre est celle qui n'a pas les caprices des macros de préprocesseur C), avec potentiellement de nombreuses optimisations disponibles.
curiousguy
3

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.

Ian
la source
2

Voyons ce que dit la norme (surligné les parties importantes en gras):

2. Une déclaration de fonction avec un spécificateur en ligne déclare une fonction en ligne. Le spécificateur en ligne indique à l'implémentation que la substitution en ligne du corps de fonction au point d'appel doit être préférée au mécanisme d'appel de fonction habituel. Une implémentation n'est pas requise pour effectuer cette substitution en ligne au point d'appel; cependant, même si cette substitution en ligne est omise, les autres règles pour les fonctions en ligne doivent toujours être respectées.

- Norme C ++, ISO / IEC 14882: 2003 , 7.1.2 Spécificateurs de fonctions [dcl.fct.spec]

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:

Il n'y a pas de réponses simples: vous devez jouer avec pour voir ce qui est le mieux. Ne vous contentez pas de réponses simplistes comme «Ne jamais utiliser de inlinefonctions» ou «Toujours utiliser des inlinefonctions» ou «Utiliser des inlinefonctions si et seulement si la fonction est inférieure à N lignes de code». Ces règles uniformes peuvent être faciles à écrire, mais elles produiront des résultats sous-optimaux.

- FAQ C ++, Fonctions en ligne , 9.3 Les inlinefonctions améliorent-elles les performances?

Tamara Wijsman
la source
1
Ce que vous dites est vrai, mais non pertinent, car en tant que programmeur, ce n'est pas à vous de décider si vous souhaitez vous connecter ou non. Vous mettez le mot-clé, vous pouvez ou non obtenir vos fonctions en ligne. Vous ne mettez pas le mot-clé, vous pouvez toujours ou non les mettre en ligne. Il est inutile d'avoir des règles pour quand vous mettez le mot-clé, le compilateur étant celui qui prend la décision.
Kate Gregory
En quoi cela n'est-il pas pertinent s'il dit exactement la même chose que vous? Il n'y a pas de réponses simples .
Tamara Wijsman
Ce n'est pas pertinent car il ne répond pas à la question des PO. Il ne demande pas "que fait l'inline", il demande "quel est le point". Et votre réponse ne fait que réitérer ce que nous savons tous déjà en ligne.
Davor Ždralo
@Davor: "Quel est l'intérêt?" ne respecte pas la FAQ, donc je réponds aux questions posées dans la question. Je dis le point inlineen 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 ...
Tamara Wijsman
Pourquoi diable ne respecterait-il pas la FAQ? La norme décrit la syntaxe et la sémantique des mots clés en ligne, et la question des OP est de savoir à quoi cela sert, quand doit-elle être utilisée, quels problèmes est-elle censée résoudre? Je ne vois pas comment cela ne respecte pas la FAQ.
Davor Ždralo
1

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.

Max Kielland
la source
Eh quoi? Mettre en ligne dans votre code ne signifie pas que le compilateur va réellement l'inclure, et ne pas le mettre là ne signifie pas non plus qu'il ne le fera pas. C'est juste un indice, qui est principalement ignoré par les compilateurs d'aujourd'hui. Si le compilateur trouve utile de s'aligner, alors il le sera, sinon, il ne le fera pas. Vous n'y avez aucun contrôle réel. OP le sait déjà et demande, je cite: "À quoi ça sert?". Qui diable donne +1 à ce sujet? C'est à la fois trompeur (ce qui implique que le mot-clé en ligne impose la mise en ligne) et non pertinent pour la question.
Davor Ždralo
2
@Davor Ždralo Pas besoin d'être impoli. J'ai interprété la question comme POURQUOI devrais-je utiliser des inlines? Mon but était de montrer que vous pouvez obtenir le code plus rapidement au prix de la mémoire. Chaque compilateur peut gérer le mot-clé en ligne différemment, vous devez donc vérifier la documentation, que je n'ai pas mentionnée. Étant donné que vous ne pouvez pas toujours vous permettre la surcharge de mémoire supplémentaire créée en ligne, il est utile de "guider" quand l'utiliser. Je n'ai pas non plus dit que les inlines sont appliquées. Quelqu'un a trouvé cette réponse utile pour lui et a voté +1, veuillez respecter le fait que d'autres peuvent avoir un autre niveau d'expérience et trouver quelque chose de trivial utile.
Max Kielland