Quand utiliser la fonction en ligne et quand ne pas l'utiliser?

185

Je sais que inline est un indice ou une demande au compilateur et qu'il est utilisé pour éviter les frais généraux d'appel de fonction.

Alors sur quelle base peut-on déterminer si une fonction est candidate ou non à l'inlining? Dans quel cas faut-il éviter l'inlining?

Ashish
la source
11
inlineest au nouveau venu C ++ ce que CFLAGSsont au nouveau venu Gentoo: non, compiler avec -O3 -funroll-loops -finline-functionsne fera pas voler votre ancien Pentium;)
Gregory Pakosz
1
Une raison de ne pas utiliser en ligne est que certains débogueurs ne vous permettront pas de définir un point d'arrêt ou d'entrer dans une fonction en ligne.
Rob deFriesse
1
Dupliquer: stackoverflow.com/questions/1875947/…
Steve Jessop
5
Vous ne devez pas déterminer si une fonction doit être intégrée ou non. Laissez le compilateur le faire; il est meilleur que vous (et peut intégrer des fonctions de manière sélective en fonction de l'environnement de chaque appel).
David Thornley
@DavidThornley Parfois, même avec un indicateur O3 défini, le compilateur n'inline pas la fonction si la définition est dans un fichier cpp. Donc, la règle empirique que je suis est de mettre en ligne un liners et aussi ces fonctions sans aucune boucle.
talekeDskobeDa

Réponses:

210

Éviter le coût d'un appel de fonction n'est que la moitié de l'histoire.

faire:

  • utiliser inlineau lieu de#define
  • les très petites fonctions sont de bons candidats pour inline: un code plus rapide et des exécutables plus petits (plus de chances de rester dans le cache de code)
  • la fonction est petite et appelée très souvent

ne pas:

  • fonctions volumineuses: conduit à des exécutables plus volumineux, ce qui nuit considérablement aux performances, quelle que soit l'exécution plus rapide résultant de la surcharge d'appel
  • fonctions en ligne liées aux E / S
  • la fonction est rarement utilisée
  • constructeurs et destructeurs: même vide, le compilateur génère du code pour eux
  • rupture de la compatibilité binaire lors du développement de bibliothèques:
    • en ligne une fonction existante
    • modifier une fonction inline ou rendre une fonction inline non-inline: la version précédente de la bibliothèque appelle l'ancienne implémentation

lors du développement d'une bibliothèque, afin de rendre une classe extensible à l'avenir, vous devez:

  • ajouter un destructeur virtuel non en ligne même si le corps est vide
  • rendre tous les constructeurs non en ligne
  • écrire des implémentations non en ligne du constructeur de copie et de l'opérateur d'affectation sauf si la classe ne peut pas être copiée par valeur

Rappelez-vous que le inlinemot-clé est une indication pour le compilateur: le compilateur peut décider de ne pas insérer une fonction et il peut décider d'insérer des fonctions qui n'ont pas été marquées inlineen premier lieu. J'évite généralement la fonction de marquage inline(à part peut-être lors de l'écriture de très très petites fonctions).

Concernant les performances, l'approche judicieuse est (comme toujours) de profiler l'application, puis éventuellement inlineun ensemble de fonctions représentant un goulot d'étranglement.

Références:


EDIT: Bjarne Stroustrup, le langage de programmation C ++:

Une fonction peut être définie comme étant inline. Par exemple:

inline int fac(int n)
{
  return (n < 2) ? 1 : n * fac(n-1);
}

Le inlinespécificateur est un indice pour le compilateur qu'il devrait tenter de générer du code pour un appel en fac()ligne plutôt que de définir le code de la fonction une fois, puis d'appeler via le mécanisme d'appel de fonction habituel. Un compilateur intelligent peut générer la constante 720d'un appel fac(6). La possibilité de fonctions en ligne mutuellement récursives, de fonctions en ligne qui se répètent ou non en fonction de l'entrée, etc., rend impossible de garantir que chaque appel d'une inlinefonction est effectivement en ligne. Le degré d'intelligence d'un compilateur ne peut pas être légiféré, donc un compilateur peut générer 720, un autre 6 * fac(5)et encore un autre un appel non intégré fac(6).

Pour rendre l'inlining possible en l'absence de fonctionnalités de compilation et de liaison inhabituellement intelligentes, la définition - et pas seulement la déclaration - d'une fonction inline doit être dans la portée (§9.2). Un inlineespecifier n'affecte pas la sémantique d'une fonction. En particulier, une fonction en ligne a toujours une adresse unique et a donc des staticvariables (§7.1.2) d'une fonction en ligne.

EDIT2: ISO-IEC 14882-1998, 7.1.2 Spécificateurs de fonction

Une déclaration de fonction (8.3.5, 9.3, 11.4) avec un inlinespécificateur déclare une fonction en ligne. Le spécificateur en ligne indique à l'implémentation que la substitution en ligne du corps de la 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 nécessaire 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 définies au 7.1.2 doivent toujours être respectées.

Gregory Pakosz
la source
34
inlineest bien plus qu'un indice pour le compilateur. Il modifie les règles linguistiques sur plusieurs définitions. De plus, avoir des données statiques n'est pas une raison pour éviter d'incruster une fonction. L'implémentation est obligée d'allouer un seul objet statique pour chaque fonction statique que la fonction soit déclarée inlineou non. Les classes sont toujours extensibles si elles ont des constructeurs en ligne et des destructeurs virtuels. Et le destructeur d'accolades vides est la seule fonction virtuelle qu'il est parfois judicieux de laisser en ligne.
CB Bailey
2
C'est un indice dans le sens où la fonction ne finit pas nécessairement par être intégrée (mais l'anglais n'est pas ma langue maternelle). À propos de la statique dans les fonctions marquées inline, le résultat est que la fonction n'est pas incorporée: vous payez le prix de l'appel et chaque unité de traduction qui inclut et appelle la fonction obtient sa propre copie du code et des variables statiques. La raison de ne pas intégrer les constructeurs et les destructeurs lors du développement d'une bibliothèque est la compatibilité binaire avec les futures versions de votre bibliothèque
Gregory Pakosz
14
Il est inexact de l'appeler un "indice au compilateur". En réalité, les non- inlinefonctions peuvent être intégrées si le compilateur en a envie. Et les inlinefonctions ne seront pas intégrées si le compilateur décide de ne pas les intégrer. Comme l'a dit Charles Bailey, cela change les règles linguistiques. Plutôt que de le considérer comme un indice d'optimisation, il est plus juste de le considérer comme un concept complètement différent. Le inlinemot-clé indique au compilateur d'autoriser plusieurs définitions, et rien d'autre. L'optimisation "inlining" peut être appliquée à presque toutes les fonctions, qu'elles soient marquées ou non inline.
jalf
26
C'est juste que, quand Stroustrup écrit "le spécificateur en ligne est un indice pour le compilateur", je suis surpris que je sois blâmé pour l'avoir cité. Quoi qu'il en soit, j'ai passé suffisamment de temps à faire de mon mieux pour soutenir cette réponse avec autant de références que possible
Gregory Pakosz
2
@GregoryPakosz: Mais nous n'utilisons pas tous inlinepour obtenir l'inlining de fonction. Parfois, nous voulons les autres avantages, comme contourner l'ODR.
Courses de légèreté en orbite
57

inlinea très peu à voir avec l'optimisation. inlineest une instruction au compilateur de ne pas produire d'erreur si la définition de fonction donnée se produit plusieurs fois dans le programme et une promesse que la définition se produira dans chaque traduction utilisée et partout où elle apparaîtra, elle aura exactement la même définition.

Compte tenu des règles ci-dessus, inlineconvient aux fonctions courtes dont le corps ne nécessite pas d'inclure des dépendances supplémentaires sur ce dont une simple déclaration aurait besoin. Chaque fois que la définition est rencontrée, elle doit être analysée et le code de son corps peut être généré, ce qui implique une surcharge du compilateur sur une fonction définie une seule fois dans un seul fichier source.

Un compilateur peut en ligne (c'est-à-dire remplacer un appel à la fonction par du code qui effectue cette action de cette fonction) tout appel de fonction qu'il choisit. Auparavant, il ne pouvait "évidemment" pas intégrer une fonction qui n'était pas déclarée dans la même unité de traduction que l'appel, mais avec l'utilisation croissante de l'optimisation du temps de liaison, même ce n'est plus vrai maintenant. Il est également vrai que les fonctions marquées inlinepeuvent ne pas être insérées.

CB Bailey
la source
J'ai le sentiment que c'est plus une heureuse coïncidence qu'une fonctionnalité intentionnelle de C ++. L'idée est très similaire aux variables globales «statiques» de C. C'est une réponse très intéressante cependant. J'aurais aimé qu'ils utilisent juste un mot-clé comme «interne» pour indiquer un lien interne.
Rehno Lindeque
+1. @Rehno: Je ne suis pas vraiment sûr de ce que vous dites. Qu'est-ce que le lien a à voir avec le inlinemot-clé? Et qu'est-ce qu'une heureuse coïncidence?
jalf
@jalf: En lisant mon commentaire rétrospectivement, je me rends compte qu'il est plutôt vague et pas si bien pensé. La définition de la même fonction dans plusieurs fichiers entraîne une erreur de l'éditeur de liens qui peut être contrée en déclarant la fonction «statique». Cependant, «en ligne» vous permet de faire la même chose avec des différences subtiles qu'ils n'obtiennent pas réellement de lien interne comme le fait «statique». Je soupçonne que c'est en fait plus une coïncidence parce que les développeurs / concepteurs de langage ont réalisé qu'ils devront faire quelque chose de spécial avec les fonctions déclarées dans les fichiers d'en-tête et qui sont reportées sur «en ligne».
Rehno Lindeque
4
Je ne sais pas pourquoi votre commentaire a reçu autant de votes, car les performances sont la principale raison d'utiliser en ligne.
gast128
10

Dire au compilateur d'incorporer une fonction est une optimisation, et la règle la plus importante de l'optimisation est que l'optimisation prématurée est la racine de tout mal. Écrivez toujours du code clair (en utilisant des algorithmes efficaces), puis profilez votre programme et n'optimisez que les fonctions qui prennent trop de temps.

Si vous trouvez qu'une fonction particulière est très courte et simple, et qu'elle est appelée des dizaines de milliers de fois dans une boucle interne étroite, cela pourrait être un bon candidat.

Vous pourriez être surpris, cependant - de nombreux compilateurs C ++ intégreront automatiquement de petites fonctions pour vous - et ils pourraient également ignorer votre demande d'inline.

dmazzoni
la source
En effet, je soupçonne que certains compilateurs ignorent très sournoisement «inline» complètement et ne répondent qu'à «__inline» ou «__force_inline». Je suppose que c'est pour dissuader les abus!
Rehno Lindeque
Ce n'est généralement pas le cas. inline n'est qu'un indice, mais c'est un indice que la plupart des compilateurs prennent au sérieux. Vous pouvez configurer le compilateur pour qu'il émette le langage d'assemblage avec le code objet ( /FAcsdans Visual Studio, -sdans GCC) pour voir exactement ce qu'il fait. D'après mon expérience, ces deux compilateurs pèsent assez lourdement sur le mot-clé en ligne.
Crashworks
1
C'est intéressant, car d'après mon expérience, ni g ++ ni VC ne pèsent du tout le inlinemot-clé. Autrement dit, si vous voyez la fonction en cours d'inclusion et que vous en supprimez le inlinespécificateur, elle sera toujours incorporée. Si vous avez des exemples spécifiques du contraire, partagez-les!
Pavel Minaev
4
comment le inlinemot clé empêche-t-il le "code clair"? Le mot clé "optimisation prématurée" est prématuré et non optimisation. Dire que vous devriez activement * éviter les optimisations n'est que de la bêtise. Le but de cette citation est que vous devez éviter les optimisations qui peuvent ne pas être nécessaires et avoir des effets secondaires néfastes sur le code (comme le rendre moins maintenable). Je ne vois pas comment le inlinemot-clé va rendre le code moins maintenable, ou comment il peut être dangereux de l'ajouter à une fonction.
jalf
3
jalf, parfois l'intégration d'une fonction rendra votre code plus lent, pas plus rapide. Un exemple est lorsque la fonction est appelée à partir de plusieurs endroits différents dans votre code; si la fonction n'est pas en ligne, alors il se peut qu'elle soit toujours dans le cache d'instructions lorsqu'elle est appelée depuis un endroit différent, et le prédicteur de branche peut déjà être réchauffé. Certains modèles améliorent toujours l'efficacité, donc cela ne fait jamais de mal de les utiliser. L'inlining n'en fait pas partie. Cela n'a généralement aucun effet sur les performances, cela aide parfois et parfois fait mal. Je suis derrière mon conseil: profil d'abord, puis en ligne.
dmazzoni
5

La meilleure façon de le savoir est de profiler votre programme et de marquer les petites fonctions qui sont appelées de nombreuses fois et de graver les cycles du processeur comme inline. Le mot clé ici est "petit" - une fois que la surcharge de l'appel de fonction est négligeable par rapport au temps passé dans la fonction, il est inutile de les intégrer.

L'autre utilisation que je suggérerais est que si vous avez de petites fonctions qui sont appelées suffisamment souvent dans le code critique pour que le cache soit pertinent, vous devriez probablement les intégrer également. Encore une fois, c'est quelque chose que le profileur devrait être en mesure de vous dire.

Timo Geusch
la source
4

L'optimisation prématurée est la racine de tout Mal!

En règle générale, je n'inline généralement que des "getters" et des "setters". Une fois que le code fonctionne et est stable, le profilage peut montrer quelles fonctions pourraient bénéficier de l'inlining.

D'un autre côté, la plupart des compilateurs modernes ont d'assez bons algorithmes d'optimisation et incorporeront ce que vous auriez dû incorporer pour vous.

Reasuming - écrivez des fonctions en ligne sur une seule ligne et vous inquiétez des autres plus tard.

Kornel Kisielewicz
la source
2

Les fonctions en ligne peuvent améliorer les performances de votre code en éliminant le besoin de pousser des arguments dans la pile. si la fonction en question est dans une partie critique de votre code, vous devez prendre la décision inline not inline dans la partie optimisation de votre projet,

vous pouvez en savoir plus sur les inlines dans la FAQ c ++

Alon
la source
1

J'utilise souvent des fonctions en ligne non pas comme une optimisation mais pour rendre le code plus lisible. Parfois, le code lui-même est plus court et plus facile à comprendre que les commentaires, les noms descriptifs, etc. Par exemple:

void IncreaseCount() { freeInstancesCnt++; }

Le lecteur connaît immédiatement la sémantique complète du code.

Danatel
la source
0

Je suis généralement une règle empirique où je crée une fonction avec 3-4 instructions simples en ligne. Mais il est bon de se rappeler qu'il ne s'agit que d'un indice pour le compilateur. L'appel final pour le rendre en ligne ou non est pris par le compilateur uniquement. S'il y a plus que ces nombreuses déclarations, je ne déclarerai pas en ligne, car avec un compilateur stupide, cela peut conduire à un gonflement du code.

Naveen
la source
0

Le meilleur moyen serait d'examiner et de comparer les instructions générées pour les instructions en ligne et non en ligne. Cependant, il est toujours prudent d'omettre inline. L'utilisation inlinepeut entraîner des problèmes dont vous ne voulez pas.

marcher
la source
0

Lorsque je décide d'utiliser ou non en ligne, je garde généralement l'idée suivante à l'esprit: sur les machines modernes, la latence de la mémoire peut être un goulot d'étranglement plus important que les calculs bruts. On sait que les fonctions incorporées souvent appelées augmentent la taille de l'exécutable. En outre, une telle fonction pourrait être stockée dans le cache de code du CPU, ce qui diminuera le nombre de défauts de cache lorsque ce code doit être accédé.

Par conséquent, vous devez décider vous-même: est-ce que l'inlining augmente ou diminue la taille du code machine généré? Quelle est la probabilité que l'appel de la fonction entraîne un échec du cache? Si cela est répandu dans tout le code, je dirais que la probabilité est élevée. S'il est limité à une seule boucle serrée, la probabilité est, espérons-le, faible.

J'utilise généralement l'inlining dans les cas que j'énumère ci-dessous. Cependant, lorsque vous êtes réellement préoccupé par les performances, le profilage est essentiel. De plus, vous voudrez peut-être vérifier si le compilateur prend réellement l'indication.

  • Des routines courtes qui sont appelées en boucle serrée.
  • Fonctions d'accesseurs (get / set) et wrapper très basiques.
  • Le code de modèle dans les fichiers d'en-tête obtient malheureusement automatiquement l'indication en ligne.
  • Code court utilisé comme une macro. (Par exemple, min () / max ())
  • Courtes routines mathématiques.
Rehno Lindeque
la source
0

En outre, une méthode en ligne a des effets secondaires graves lors de la maintenance de grands projets. Lorsque le code en ligne est modifié, tous les fichiers qui l'utilisent seront reconstruits automatiquement par le compilateur (c'est un bon compilateur). Cela pourrait vous faire perdre beaucoup de temps de développement.

Lorsqu'une inlineméthode est transférée dans un fichier source et n'est plus en ligne, l'ensemble du projet doit être reconstruit (du moins c'est mon expérience). Et aussi lorsque les méthodes sont converties en inline.

Thomas Matthews
la source
1
C'est un problème différent. Vous obtenez le problème de reconstruction pour le code qui est placé dans un fichier d'en-tête. Qu'il soit marqué inlineou non n'a pas d'importance (sauf sans le inlinemot - clé, vous obtiendrez des erreurs de l'éditeur de liens - mais le inlinemot-clé n'est pas le problème causant des reconstructions excessives.
jalf
Cependant, la modification d'une méthode en ligne entraînera des builds excessifs par rapport à la modification d'une méthode non en ligne dans un fichier shource.
Thomas Matthews
0

On ne devrait utiliser le qualificatif de fonction en ligne que lorsque le code de fonction est petit. Si les fonctions sont plus grandes, vous devriez préférer les fonctions normales car l'économie d'espace mémoire vaut le sacrifice relativement petit en vitesse d'exécution.

nitish
la source
0

Lorsque vous pensez que votre code est suffisamment petit pour être utilisé en ligne et que vous vous souvenez de la fonction en ligne, dupliquez votre code et collez-le là où la fonction est appelée, cela peut être suffisant pour augmenter votre temps d'exécution, mais aussi augmenter votre consommation de mémoire. Vous ne pouvez pas utiliser la fonction en ligne lorsque vous utilisez une fonction boucle / variable statique / récursive / commutateur / goto / virtuelle. Virtuel signifie attendre l'exécution et les moyens en ligne pendant la compilation afin qu'ils ne puissent pas être utilisés simultanément.

sachin pathak
la source
-2

J'ai lu quelques réponses et je constate qu'il manque des choses.

La règle que j'utilise est de ne pas utiliser en ligne, sauf si je veux qu'elle soit en ligne. Ça a l'air idiot, maintenant l'explication.

Les compilateurs sont assez intelligents et les fonctions courtes font toujours en ligne. Et ne fait jamais fonctionner long en ligne, à moins que le programmeur ne dise de le faire.

Je sais que inline est un indice ou une demande au compilateur

En fait, inlinec'est une commande pour le compilateur, il n'a pas de choix et après le inlinemot clé rend tout le code en ligne. Ainsi, vous ne pouvez jamais utiliser de inlinemot-clé et le compilateur concevra le code le plus court.

Alors quand l'utiliser inline?

À utiliser si vous souhaitez avoir du code en ligne. Je ne connais qu'un seul exemple, car je ne l'utilise que dans une seule situation. C'est l'authentification de l'utilisateur.

Par exemple, j'ai cette fonction:

inline bool ValidUser(const std::string& username, const std::string& password)
{
    //here it is quite long function
}

Quelle que soit la taille de cette fonction, je veux l'avoir en ligne car cela rend mon logiciel plus difficile à déchiffrer.

ST3
la source
2
inline est toujours un indice. Le compilateur peut échouer en ligne s'il estime que votre fonction est trop gonflée.
C'est le
L'un dit en ligne est un ordre ... l'autre dit que c'est un indice Est-ce que quelqu'un justifierait sa déclaration afin que nous puissions déterminer laquelle est vraie?
@ user2918461 Je supporte la déclaration en ligne n'est qu'un indice. Cela a été soutenu par de nombreux sites Web et livres
WARhead