Avantages des fonctions en ligne en C ++?

254

Quels sont les avantages / inconvénients de l'utilisation des fonctions en ligne en C ++? Je vois que cela n'augmente que les performances du code généré par le compilateur, mais avec les compilateurs optimisés d'aujourd'hui, les processeurs rapides, la mémoire énorme, etc. (pas comme dans les années 1980 <où la mémoire était rare et tout devait tenir dans 100 Ko de mémoire) ont-ils vraiment des avantages aujourd'hui?

Lennie De Villiers
la source
48
C'est l'une de ces questions où la connaissance commune est erronée. Tout le monde a répondu avec la réponse standard Comp Sci. (L'intégration économise les coûts des appels de fonction mais augmente la taille du code). Ordures. Il fournit un mécanisme simple pour que le compilateur applique plus d'OPTIMISATIONS.
Martin York,
37
C'est l'une de ces réponses se présentant comme des commentaires. Si vous n'aimez aucune des réponses qui ont été publiées, publiez votre propre réponse et voyez comment cela se passe.
Dave Van den Eynde
10
La base de cette question est défectueuse. Les fonctions en ligne C ++ ont peu à voir avec les compilateurs en ligne pendant la compilation. Il est regrettable qu'il inlines'agisse d'un mot clé c ++ et que l'incrustation soit une technique d'optimisation du compilateur. Voir cette question " quand dois-je écrire le mot-clé inlinepour une fonction / méthode " pour la bonne réponse.
deft_code
3
@JoseVega Votre lien a été modifié - le lien actuel est exforsys.com/tutorials/c-plus-plus/inline-functions.html

Réponses:

143

Les fonctions en ligne sont plus rapides car vous n'avez pas besoin de pousser et de faire sauter des choses sur / hors de la pile comme les paramètres et l'adresse de retour; cependant, cela rend votre binaire légèrement plus grand.

Cela fait-il une différence significative? Pas assez sur le matériel moderne pour la plupart. Mais cela peut faire une différence, ce qui est suffisant pour certaines personnes.

Marquer quelque chose en ligne ne vous donne pas la garantie qu'il sera en ligne. C'est juste une suggestion pour le compilateur. Parfois, ce n'est pas possible, par exemple lorsque vous avez une fonction virtuelle ou lorsqu'il y a récursivité. Et parfois, le compilateur choisit simplement de ne pas l'utiliser.

Je pouvais voir une situation comme celle-ci faire une différence détectable:

inline int aplusb_pow2(int a, int b) {
  return (a + b)*(a + b) ;
}

for(int a = 0; a < 900000; ++a)
    for(int b = 0; b < 900000; ++b)
        aplusb_pow2(a, b);
Brian R. Bondy
la source
26
Comme je le soupçonnais, la doublure ne fait aucune différence avec ce qui précède. Compilé avec gcc 4.01. Version 1 forcée à utiliser l'inlining: 48.318u 1.042s 5: 51.39 99.4% 0 + 0k 0 + 0io 0pf + 0w un bon exemple était la notoriété publique est faux.
Martin York
36
alors que l'appel lui-même est vraiment important, ce n'est que le gain mineur que vous obtenez en utilisant en ligne. Le gain majeur est que le compilateur voit maintenant où les pointeurs ne se pseudoient pas, où les variables de l'appelant se retrouvent dans l'appelé et ainsi de suite. Ainsi, l'optimisation suivante est ce qui compte le plus.
Johannes Schaub - litb
31
Cela ne fait probablement aucune différence dans cet extrait, car le résultat de la fonction n'est jamais utilisé et la fonction n'a pas d'effets secondaires. Nous constatons un gain de performance mesurable dans l'inline dans le traitement d'image.
socle
4
La raison de l'absence de différence pourrait être que le compilateur pourrait s'aligner de lui-même; ou que le code est petit, il n'y a donc aucun problème de prélecture de code.
einpoklum
3
@einpoklum Le compilateur peut même avoir optimisé la boucle entière à cause de cela.
noɥʇʎԀʎzɐɹƆ
197

Les avantages

  • En insérant votre code là où il est nécessaire, votre programme passera moins de temps dans l'appel de fonction et retournera les pièces. Il est censé rendre votre code plus rapide, même s'il augmente (voir ci-dessous). L'intégration d'accesseurs triviaux pourrait être un exemple d'inclusion efficace.
  • En le marquant comme étant en ligne, vous pouvez mettre une définition de fonction dans un fichier d'en-tête (c'est-à-dire qu'elle peut être incluse dans plusieurs unités de compilation, sans que l'éditeur de liens ne se plaint)

Désavantages

  • Cela peut augmenter la taille de votre code (c'est-à-dire si vous utilisez inline pour des fonctions non triviales). En tant que tel, il pourrait provoquer des optimisations de pagination et de défaite à partir du compilateur.
  • Il casse légèrement votre encapsulation car il expose l'interne de votre traitement d'objet (mais alors, chaque membre "privé" le ferait aussi). Cela signifie que vous ne devez pas utiliser d'inlining dans un modèle PImpl.
  • Il casse légèrement votre encapsulation 2: l'incrustation C ++ est résolue au moment de la compilation. Ce qui signifie que si vous changez le code de la fonction en ligne, vous devrez recompiler tout le code en l'utilisant pour être sûr qu'il sera mis à jour (pour la même raison, j'évite les valeurs par défaut pour les paramètres de la fonction)
  • Lorsqu'il est utilisé dans un en-tête, il agrandit votre fichier d'en-tête et dilue ainsi les informations intéressantes (comme la liste des méthodes d'une classe) avec du code dont l'utilisateur ne se soucie pas (c'est la raison pour laquelle je déclare des fonctions intégrées dans un , mais le définira dans un en-tête après le corps de classe, et jamais à l'intérieur du corps de classe).

Inlining Magic

  • Le compilateur peut ou non intégrer les fonctions que vous avez marquées comme intégrées; il peut également décider d'inclure des fonctions non marquées comme étant en ligne au moment de la compilation ou de la liaison.
  • Inline fonctionne comme un copier / coller contrôlé par le compilateur, ce qui est assez différent d'une macro préprocesseur: la macro sera insérée de force, polluera tous les espaces de noms et le code, ne sera pas facilement débogable et sera effectuée même si le compilateur l'aurait jugé inefficace.
  • Chaque méthode d'une classe définie à l'intérieur du corps de la classe elle-même est considérée comme "inline" (même si le compilateur peut toujours décider de ne pas l'inline)
  • Les méthodes virtuelles ne sont pas censées être inlinables. Pourtant, parfois, lorsque le compilateur peut connaître avec certitude le type de l'objet (c'est-à-dire que l'objet a été déclaré et construit à l'intérieur du même corps de fonction), même une fonction virtuelle sera alignée car le compilateur connaît exactement le type de l'objet.
  • Les méthodes / fonctions de modèle ne sont pas toujours intégrées (leur présence dans un en-tête ne les rendra pas automatiquement intégrées).
  • La prochaine étape après "inline" est la métaprogrammation du modèle. C'est-à-dire qu'en "insérant" votre code au moment de la compilation, parfois, le compilateur peut déduire le résultat final d'une fonction ... Ainsi, un algorithme complexe peut parfois être réduit à une sorte de return 42 ;déclaration. C'est pour moi une inline extrême . Cela se produit rarement dans la vie réelle, cela allonge le temps de compilation, ne gonflera pas votre code et accélérera votre code. Mais comme le Graal, n'essayez pas de l'appliquer partout car la plupart des traitements ne peuvent pas être résolus de cette façon ... Pourtant, c'est cool quand même ...
    :-p
paercebal
la source
vous avez dit que cela brise légèrement votre encapsulation. Pourriez-vous l'expliquer à l'aide d'un exemple?
Destructor
6
@PravasiMeet: C'est C ++. Supposons que vous fournissiez une DLL / bibliothèque partagée à un client, qui le compile. La fonction en ligne foo, en utilisant la variable membre X et en faisant le travail Y, sera intégrée dans le code du client. Supposons que vous deviez fournir une version mise à jour de votre DLL dans laquelle vous avez modifié la variable membre en Z et ajouter un travail YY en plus du travail Y. Le client copie simplement la DLL dans son projet et BOOM, car le code de foo dans leur binaire n'est pas le code mis à jour que vous avez écrit ... Bien que le client n'ait pas légalement accès à votre code privé, l'inline le rend assez "public".
paercebal
@paercebal En ce qui concerne votre avant-dernier point, pouvez-vous donner un exemple de cas où un modèle de fonction n'est pas en ligne? Je pensais qu'ils étaient toujours en ligne, même si je n'ai pas de référence à portée de main maintenant (un simple test semble le confirmer cependant).
Konrad Rudolph
En @KonradRudolph n4594 je vois: 3.2/6: There can be more than one definition of [..] inline function with external linkage [..] non-static function template. À 5.1.5 / 6 For a generic lambda, the closure type has a public inline function call operator member template. Et à l' 7.1.2/2: the use of inline keyword is to declare an inline functionendroit où il est suggéré d'insérer le corps de la fonction au point d'appel. Donc, je conclus que même si elles peuvent se comporter de la même manière, les fonctions en ligne et les modèles de fonction sont toujours des notions orthogonales distinctes qui peuvent être mélangées (par exemple, modèle de fonction en ligne)
paercebal
l'encapsulation est cassée? Comment? l'encapsulation est destinée à la programmation des gars, pas aux objets réels en mémoire. à ce moment-là, personne ne s'en soucie. Même si vous distribuez une bibliothèque, le compilateur peut choisir de s'aligner ou de ne pas le faire seul. donc à la fin, quand vous obtenez une nouvelle bibliothèque, il vous suffit de recompiler tout ce qui utilise les fonctions et les objets de cette bibliothèque.
FalcoGer
42

En archaïque C et C ++, inlinec'est comme register: une suggestion (rien de plus qu'une suggestion) au compilateur sur une éventuelle optimisation.

En C ++ moderne, inlineindique à l'éditeur de liens que, si plusieurs définitions (et non des déclarations) sont trouvées dans différentes unités de traduction, elles sont toutes identiques, et l'éditeur de liens peut librement en conserver une et éliminer toutes les autres.

inline est obligatoire si une fonction (aussi complexe ou "linéaire") soit définie dans un fichier d'en-tête, pour permettre à plusieurs sources de l'inclure sans obtenir une erreur de "définition multiple" par l'éditeur de liens.

Les fonctions membres définies dans une classe sont "en ligne" par défaut, tout comme les fonctions de modèle (contrairement aux fonctions globales).

//fileA.h
inline void afunc()
{ std::cout << "this is afunc" << std::endl; }

//file1.cpp
#include "fileA.h"
void acall()
{ afunc(); }

//main.cpp
#include "fileA.h"
void acall();

int main()
{ 
   afunc(); 
   acall();
}

//output
this is afunc
this is afunc

Notez l'inclusion de fileA.h dans deux fichiers .cpp, résultant en deux instances de afunc(). L'éditeur de liens supprimera l'un d'eux. Si non inlineest spécifié, l'éditeur de liens se plaindra.

Emilio Garavaglia
la source
16

Inlining est une suggestion au compilateur qu'il est libre d'ignorer. Il est idéal pour les petits morceaux de code.

Si votre fonction est en ligne, elle est essentiellement insérée dans le code où l'appel de fonction lui est fait, plutôt que d'appeler réellement une fonction distincte. Cela peut vous aider avec la vitesse car vous n'avez pas à faire l'appel réel.

Il assiste également les processeurs avec le pipelining car ils n'ont pas à recharger le pipeline avec de nouvelles instructions provoquées par un appel.

Le seul inconvénient est l'augmentation possible de la taille binaire mais, tant que les fonctions sont petites, cela n'aura pas trop d'importance.

J'ai tendance à laisser ce genre de décisions aux compilateurs de nos jours (enfin, les plus intelligents de toute façon). Les personnes qui les ont écrites ont généralement une connaissance beaucoup plus détaillée des architectures sous-jacentes.

paxdiablo
la source
12

La fonction en ligne est la technique d'optimisation utilisée par les compilateurs. On peut simplement ajouter un mot clé en ligne au prototype de fonction pour créer une fonction en ligne. La fonction inline demande au compilateur d'insérer le corps complet de la fonction partout où cette fonction a été utilisée dans le code.

Avantages: -

  1. Il ne nécessite pas de fonction appelant la surcharge.

  2. Il enregistre également les frais généraux des variables push / pop sur la pile, lors de l'appel de fonction.

  3. Il enregistre également les frais généraux de l'appel de retour d'une fonction.

  4. Il augmente la localité de référence en utilisant le cache d'instructions.

  5. Après compilation en ligne, le compilateur peut également appliquer une optimisation intra-procédurale si spécifié. C'est le plus important, de cette façon le compilateur peut désormais se concentrer sur l'élimination du code mort, peut mettre plus de stress sur la prédiction de branche, l'élimination des variables d'induction, etc.

Pour en savoir plus, on peut suivre ce lien http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html

TSS
la source
4
1) C'est une suggestion, pas une instruction 2) Cela peut provoquer plus de ratés de cache en raison de l'augmentation de la taille du code si une fonction couramment utilisée est souvent en ligne
Flexo
3
Le lien à la fin est-il votre blog personnel? Si c'est le cas, vous devez le déclarer comme tel, sinon il ressemble à du spam.
Flexo
6

Je voudrais ajouter que les fonctions en ligne sont cruciales lorsque vous créez une bibliothèque partagée. Sans marquer la fonction en ligne, elle sera exportée dans la bibliothèque sous forme binaire. Il sera également présent dans le tableau des symboles, s'il est exporté. De l'autre côté, les fonctions intégrées ne sont pas exportées, ni vers les binaires de la bibliothèque ni vers la table des symboles.

Cela peut être critique lorsque la bibliothèque est destinée à être chargée au moment de l'exécution. Il peut également toucher des bibliothèques compatibles avec les binaires. Dans de tels cas, n'utilisez pas inline.

doc
la source
@Johnsyweb: lisez attentivement ma réponse. Ce que vous avez dit est vrai lorsque vous construisez un exécutable. Mais le compilateur ne peut pas simplement ignorer inlinelors de la construction d'une bibliothèque partagée!
doc
4

Pendant l'optimisation, de nombreux compilateurs intègrent des fonctions même si vous ne les avez pas marquées. Vous n'avez généralement besoin de marquer les fonctions comme étant en ligne que si vous savez quelque chose que le compilateur ne sait pas, car il peut généralement prendre la bonne décision lui-même.

Devrin
la source
De nombreux compilateurs ne le feront pas non plus, MSVC par exemple ne le fera pas sauf si vous le lui dites
paulm
4

inlinevous permet de placer une définition de fonction dans un fichier d'en-tête et #includece fichier d'en-tête dans plusieurs fichiers source sans violer la règle d'une définition.

Ferruccio
la source
3

De manière générale, ces jours-ci, tout compilateur moderne se souciant d'inclure quoi que ce soit est à peu près une perte de temps. Le compilateur doit en fait optimiser toutes ces considérations pour vous grâce à sa propre analyse du code et à votre spécification des indicateurs d'optimisation passés au compilateur. Si vous vous souciez de la vitesse, dites au compilateur d'optimiser la vitesse. Si vous vous souciez de l'espace, dites au compilateur d'optimiser l'espace. Comme une autre réponse a fait allusion, un compilateur décent s'alignera même automatiquement si cela a vraiment du sens.

De plus, comme d'autres l'ont dit, l'utilisation de l'inline ne garantit rien de l'inline. Si vous voulez le garantir, vous devrez définir une macro au lieu d'une fonction inline pour le faire.

Quand aligner et / ou définir une macro pour forcer l'inclusion? - Seulement lorsque vous avez une augmentation de vitesse démontrée et nécessaire pour une section critique de code qui est connue pour avoir un effet sur les performances globales de l'application.

Grand Jeff
la source
… Si vous vous souciez de l'espace, dites au compilateur d'optimiser pour l'espace - dire au compilateur d'optimiser pour la vitesse peut entraîner des binaires plus petits avec C ++ et C. de même, dire au compilateur d'optimiser pour l'espace peut entraîner une exécution plus rapide. iow, ces fonctionnalités ne fonctionnent pas toujours comme annoncé. les humains ont la capacité de mieux comprendre certains aspects de leur programme que les interprétations généralisées d'un compilateur (qui doivent également rester utilisables rapidement). l'intervention humaine ne doit pas être une mauvaise chose.
juste le
3

Ce n'est pas qu'une question de performance. C ++ et C sont tous deux utilisés pour la programmation intégrée, assis sur le matériel. Si vous voulez, par exemple, écrire un gestionnaire d'interruption, vous devez vous assurer que le code peut être exécuté en une seule fois, sans que des registres et / ou des pages mémoire supplémentaires ne soient échangés. C'est à ce moment-là que l'inline est utile. Les bons compilateurs font eux-mêmes un "alignement" lorsque la vitesse est nécessaire, mais "en ligne" les oblige.

JM Stoorvogel
la source
1

Tombé dans le même problème avec les fonctions en ligne dans les bibliothèques so. Il semble que les fonctions intégrées ne soient pas compilées dans la bibliothèque. en conséquence, l'éditeur de liens émet une erreur de "référence non définie", si un exécutable souhaite utiliser la fonction intégrée de la bibliothèque. (m'est arrivé de compiler la source Qt avec gcc 4.5.

Martin Wilde
la source
1

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.

mehrdad zomorodiyan
la source
1

Notre professeur d'informatique nous a exhortés à ne jamais utiliser inline dans un programme c ++. Lorsqu'on lui a demandé pourquoi, il nous a gentiment expliqué que les compilateurs modernes devraient détecter quand utiliser en ligne automatiquement.

Donc oui, l'inline peut être une technique d'optimisation à utiliser dans la mesure du possible, mais apparemment, c'est quelque chose qui est déjà fait pour vous chaque fois qu'il est possible d'inline une fonction de toute façon.

HumbleWebDev
la source
5
Votre professeur a malheureusement tout à fait tort. inlineen C ++ a deux significations très distinctes - une seule d'entre elles est liée à l'optimisation, et votre professeur a raison à cet égard. Cependant, le deuxième sens de inlineest souvent nécessaire pour satisfaire la règle de définition unique .
Konrad Rudolph
-1

Conclusion d' une autre discussion ici:

Y a-t-il des inconvénients avec les fonctions en ligne?

Apparemment, il n'y a rien de mal à utiliser les fonctions en ligne.

Mais il convient de noter les points suivants!

  • La surutilisation de l'inline peut en fait ralentir les programmes. Selon la taille d'une fonction, le fait de l'intégrer peut entraîner une augmentation ou une diminution de la taille du code. L'intégration d'une très petite fonction d'accesseur diminue généralement la taille du code, tandis que l'inclusion d'une très grande fonction peut augmenter considérablement la taille du code. Sur les processeurs modernes, un code plus petit s'exécute généralement plus rapidement en raison d'une meilleure utilisation du cache d'instructions. - Consignes Google

  • Les avantages de vitesse des fonctions en ligne ont tendance à diminuer à mesure que la fonction grandit. À un moment donné, la surcharge de l'appel de fonction devient faible par rapport à l'exécution du corps de fonction, et l'avantage est perdu - Source

  • Il existe peu de situations où une fonction en ligne peut ne pas fonctionner:

    • Pour une fonction renvoyant des valeurs; si une déclaration de retour existe.
    • Pour une fonction ne renvoyant aucune valeur; s'il existe une instruction loop, switch ou goto.
    • Si une fonction est récursive. -La source
  • Le __inlinemot clé entraîne la mise en ligne d'une fonction uniquement si vous spécifiez l'option d'optimisation. Si optimiser est spécifié, le respect ou non __inlinedépend du réglage de l'option d'optimisation en ligne. Par défaut, l'option en ligne est en vigueur chaque fois que l'optimiseur est exécuté. Si vous spécifiez optimiser, vous devez également spécifier l'option noinline si vous souhaitez que le __inlinemot clé soit ignoré. -La source

prakash
la source
3
Serait vrai si l'inline était une commande et non un indice pour le compilateur. Le compilateur décide réellement ce qu'il faut incorporer.
Martin York
1
@LokiAstari Je sais que l'inline est une requête au compilateur. Mon argument est Si son indice pour le compilateur, nous devons laisser au compilateur le soin de décider ce qui est le mieux. Pourquoi utiliser inline de toute façon, même si vous utilisez inline, c'est toujours le compilateur qui prendra la décision finale. Je me demande également si mon Microsoft a introduit _forceinline.
Krishna Oza
@krish_oza: Mon commentaire ici. C'est à propos de cette réponse. La réponse ici est complètement fausse. Parce que le compilateur ne tient pas compte du inlinemot clé pour déterminer s'il faut ou non coder en ligne tous les points mentionnés ci-dessus sont erronés. Ils seraient vrais si le compilateur utilisait le mot-clé pour l'inlining (il n'est utilisé que pour déterminer le marquage de plusieurs définitions à des fins de liaison).
Martin York