En informatique, l'extension inline, ou inlining, est une optimisation d'un compilateur qui remplace un appel de fonction par le code de cette fonction. Cette optimisation vise à réduire le temps d'exécution ainsi que la consommation mémoire. Toutefois, l'extension inline peut augmenter la taille du programme (par la répétition du code d'une fonction).

Certains langages (par exemple le C ou le C++) ont un mot clé inline attachable à la définition d'une fonction. Ce mot clé indique au compilateur qu'il devrait essayer d'étendre cette fonction. Le compilateur pourra ensuite utiliser plusieurs heuristiques pour décider quelle fonction doit être étendue inline.

Implémentation modifier

Dès que le compilateur décide d'étendre une fonction, l'opération est assez simple. La modification peut se faire au niveau du code source, d'une représentation intermédiaire (comme un arbre syntaxique abstrait) ou une représentation de bas niveau (bytecode ou autre).

Le principe est le calcul des valeurs des arguments, leur stockage dans des variables et finalement l'insertion du code de la fonction en lieu et place de l'appel.

Un éditeur de liens peut également implanter une extension inline. Cela permet de ne pas subir la frontière d'une bibliothèque dont le code n'est pas connu et ainsi potentiellement d'optimiser encore plus le code d'une application.

Finalement, un environnement d'exécution peut également inclure une extension inline. Par exemple, la machine virtuelle Java peut utiliser les statistiques d'exécution pour décider de fonctions à étendre.

Exemple modifier

Voici un exemple d'extension à la main sur du code en C. Admettons que l'on ait une fonction pred qui doit être appelée par une fonction f :

int pred(int x) {
    if (x == 0)
       return 0;
    else
       return x - 1;
}

Sans extension, la fonction f s'écrit :

 int f(int y) {
     return pred(y) + pred(0) + pred(y+1);
 }

La fonction f peut également s'écrire, après extension :

int f(int y) {
    int temp = 0;
    if (y   == 0) temp += 0; else temp += y       - 1; /* (1) */
    if (0   == 0) temp += 0; else temp += 0       - 1; /* (2) */
    if (y+1 == 0) temp += 0; else temp += (y + 1) - 1; /* (3) */
    return temp;
}

Il s'agit d'un exemple à la main. Pour indiquer au compilateur C que la fonction peut être étendue, il faudrait ajouter le mot clé inline dans la déclaration de la fonction pred.

Avantages modifier

Une extension inline permet en soi d'éliminer le coût d'un appel de fonction, mais son intérêt principal est de rendre possible d'autres optimisations. En effet, le remplacement de l'appel de la fonction par le code permet au compilateur des optimisations qui ne sont pas possibles à travers un appel de fonction.

Par exemple, une condition peut être analysée comme toujours vraie (ou fausse) dans le contexte d'un appel particulier ce qui peut permettre au compilateur d'éliminer le test. L'extension inline peut également rendre possible la détection de code mort, l'optimisation d'invariant, ou encore l'élimination de variables d'induction.

Dans l'exemple précédent en C, on trouve les possibilités d'optimisation suivante

  • L'instruction temp += 0 aux lignes (1), (2) et (3) ne fait rien. Le compilateur peut les enlever.
  • La condition 0 == 0 est toujours vraie. Le compilateur peut remplacer la ligne (2) par temp += 0 (qui ne fait rien et peut donc être supprimée à son tour).
  • Le compilateur peut transformer y+1 == 0 par y == -1.
  • Le compilateur peut réduire l'expression (y + 1) - 1 à y.
  • Les expressions y et y+1 ne peuvent pas être nulles en même temps. Cela permet l'élimination d'un test.

Le résultat est alors:

int f(int y) {
    if (y == 0)
        return y;            /* or return 0 */
    else if (y == -1)
        return y - 1;        /* or return -2 */
    else
        return y + y - 1;
}

Inconvénients modifier

Le remplacement effectué lors d'une extension inline peut diminuer la performance :

  • Dans les applications où la taille de l'application est importante (plus importante que le gain de performance espéré), l'extension peut être néfaste pour des fonctions longues.
  • L'accroissement de la taille du code sur une partie cruciale du code peut entraîner un dépassement de la taille du cache CPU provoquant des cache miss avec un impact global négatif sur la performance.
  • Les variables additionnelles utilisées pour représenter les paramètres nécessitent l'utilisation de plus de registres processeur, ce qui peut provoquer des accès mémoires plus nombreux.
  • La spécification d'un langage peut permettre à un programme de faire des suppositions sur les arguments des procédures. Ces dernières ne peuvent plus s'appliquer après l'inlining de la procédure.
  • Si la taille du code augmente au-delà de la taille de la mémoire vive, il peut alors causer du thrashing. Cela arrive rarement sur des serveurs sauf en cas d'inlining très agressif. Ce problème arrive plus souvent sur des plateformes aux ressources limitées (mobile, embarqué).

Les auteurs de compilateurs utilisent alors un ensemble d'heuristiques pour choisir quelles fonctions étendre dans le but d'améliorer la performance plutôt que de la dégrader.

Limitations modifier

Il n'est pas toujours possible de pratiquer une extension inline.

Le cas d'une fonction récursive est un problème. Le compilateur doit remplacer le code de la fonction à l'intérieur de son code, ce qui provoque une boucle sans fin.

Cela implique que chaque compilateur doit choisir quelle fonction étendre en fonction des mécanismes d'optimisation disponible.

Utilisation modifier

La plupart des compilateurs modernes utilisent beaucoup l'inlining car l'augmentation de la taille du code pose rarement problème et les progrès au niveau mémoire ont été plus rapides que ceux des processeurs.

L'inlining est une optimisation fondamentale pour les langages fonctionnels et objet. Dans ces cas, l'extension du code permet souvent d'appliquer de nombreuses optimisations permettant d'améliorer énormément la performance du code.

Voir aussi modifier

Liens externes modifier

Références modifier