Comme promis, je vous parle un peu de cette récente fonctionnalité de gcc, qui permet au compilateur d'optimiser à travers les unités de compilation. Le compilateur de Microsoft avait cette fonctionnalité depuis bien longtemps, et gcc était donc un petit peu à la traine, mais à chaque release, de nouvelles améliorations sont apportées à ce qui pourrait bien donner un sérieux coup de fouet aux performances (à l'exécution, parce qu'en revanche, les temps de compilation vont exploser...).
L'idée est que plutôt que de compiler chaque unité de compilation vers du code machine, le compilo s'arrête à une représentation moins bas niveau. C'est cette représentation qui est passée à l'éditeur de liens, qui peut alors refaire une passe d'optimisation en ayant l'ensemble des unités de compilation disponibles. La plus évidente des optimisations possibles est d'inliner les fonctions d'une unité de compilation vers une autre unité de compilation.
Pour comprendre ce qui se passe, je vous propose deux programmes. Voici le premier :
// p1.c int f(int a, int b) { return a + b; } int main() { return f(3, 5); }
Voici le deuxième, divisé en 2 fichiers
// p2.c int f(int a, int b); int main() { return f(3, 5); } // p2l.c int f(int a, int b) { return a + b; }Compilons le premier programme avec l'optimisation à fond : "gcc -O3 p1.c -o p1". Puis désassemblons le programme avec "objdump -d -S p1". La partie intéressante est :
00000000004003c0 <main>: 4003c0: b8 08 00 00 00 mov $0x8,%eax 4003c5: c3 retq
Sans surprises, le compilo a bien inliné le tout, et renvoie simplement la valeur calculée 8.
Maintenant, le deuxième programme: "g++ -O3 p2.c p2l.c -o p2". Le code désassemblé montre bien le passage des deux paramètres 5 et 3 suivi d'un appel de fonction, puisque la fonction "main" ne peut pas voir le contenu de la fonction f.
0000000000400480 <main>: 400480: be 05 00 00 00 mov $0x5,%esi 400485: bf 03 00 00 00 mov $0x3,%edi 40048a: e9 01 01 00 00 jmpq 400590 <_Z1fii> 0000000000400590 <_Z1fii>: 400590: 8d 04 37 lea (%rdi,%rsi,1),%eax 400593: c3 retq 400594: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 40059b: 00 00 00 40059e: 66 90 xchg %ax,%ax
Et maintenant, voyons avec la lto: g++ -flto -O3 p2.c p2l.c -o p2_lto. Ah ah! Nous retrouvons quelque chose d'identique au tout premier programme. Le compilateur a en effet été capable d'inliner la fonction f dans main, quand bien même sa définition n'était pas accessible. Ça marche!
0000000000400480 <main>: 400480: b8 08 00 00 00 mov $0x8,%eax 400485: c3 retq
Aucun commentaire:
Enregistrer un commentaire