Quand devrais-je omettre le pointeur du cadre?

Existe-t-il une optimisation substantielle lorsque vous omettez le pointeur de cadre? Si j’ai bien compris en lisant cette page, on -fomit-frame-pointer lorsque l’on veut éviter de sauvegarder, de configurer et de restaurer les pointeurs de vue.

Est-ce que cela est fait uniquement pour chaque appel de fonction et si oui, vaut-il vraiment la peine d’éviter quelques instructions pour chaque fonction? N’est-ce pas sortingvial pour une optimisation? Quelles sont les implications réelles de l’utilisation de cette option en dehors des limitations de débogage?

J’ai compilé le code C suivant avec et sans cette option

 int main(void) { int i; i = myf(1, 2); } int myf(int a, int b) { return a + b; } 

,

 # gcc -S -fomit-frame-pointer code.c -o withoutfp.s # gcc -S code.c -o withfp.s 

.

diff -u ‘ing les deux fichiers a révélé le code d’assembly suivant:

 --- withfp.s 2009-12-22 00:03:59.000000000 +0000 +++ withoutfp.s 2009-12-22 00:04:17.000000000 +0000 @@ -7,17 +7,14 @@ leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) - pushl %ebp - movl %esp, %ebp pushl %ecx - subl $36, %esp + subl $24, %esp movl $2, 4(%esp) movl $1, (%esp) call myf - movl %eax, -8(%ebp) - addl $36, %esp + movl %eax, 20(%esp) + addl $24, %esp popl %ecx - popl %ebp leal -4(%ecx), %esp ret .size main, .-main @@ -25,11 +22,8 @@ .globl myf .type myf, @function myf: - pushl %ebp - movl %esp, %ebp - movl 12(%ebp), %eax - addl 8(%ebp), %eax - popl %ebp + movl 8(%esp), %eax + addl 4(%esp), %eax ret .size myf, .-myf .ident "GCC: (GNU) 4.2.1 20070719 

Quelqu’un pourrait-il, s’il vous plaît, faire la lumière sur les points clés du code ci-dessus, où -fomit-frame-pointer a réellement fait la différence?

Edit: la sortie de objdump remplacée par celle de gcc -S

-fomit-frame-pointer permet à un registre supplémentaire d’être disponible pour une utilisation générale. Je suppose que ce n’est vraiment qu’un gros problème pour le x86 32 bits, qui manque un peu de ressources pour les registres. *

On pourrait s’attendre à ce que EBP ne soit plus sauvegardé et ajusté à chaque appel de fonction, et probablement une utilisation supplémentaire de EBP dans le code normal, et moins d’opérations de stack lorsque l’EBP est utilisé comme registre à usage général.

Votre code est beaucoup trop simple pour tirer parti des avantages de ce type d’optimisation – vous n’utilisez pas assez de registres. En outre, vous n’avez pas activé l’optimiseur, ce qui peut être nécessaire pour voir certains de ces effets.

* Les registres ISA, pas les registres de micro-architecture.

Le seul inconvénient de l’omettre est que le débogage est beaucoup plus difficile.

L’inconvénient majeur est qu’il existe un registre supplémentaire à usage général qui peut faire une grande différence en termes de performances. Évidemment, ce registre supplémentaire est utilisé uniquement lorsque cela est nécessaire (probablement dans votre fonction très simple, ce n’est pas le cas); dans certaines fonctions, cela fait plus de différence que dans d’autres.

Vous pouvez souvent obtenir un code d’assemblage plus significatif de GCC en utilisant l’argument -S pour générer l’assembly:

 $ gcc code.c -S -o withfp.s $ gcc code.c -S -o withoutfp.s -fomit-frame-pointer $ diff -u withfp.s withoutfp.s 

GCC ne se soucie pas de l’adresse, nous pouvons donc comparer les instructions réelles générées directement. Pour votre fonction feuille, cela donne:

  myf: - pushl %ebp - movl %esp, %ebp - movl 12(%ebp), %eax - addl 8(%ebp), %eax - popl %ebp + movl 8(%esp), %eax + addl 4(%esp), %eax ret 

GCC ne génère pas le code permettant de placer le pointeur de cadre sur la stack, ce qui modifie l’adresse relative des arguments transmis à la fonction sur la stack.

Profil de votre programme pour voir s’il y a une différence significative.

Ensuite, décrivez votre processus de développement. Le débogage est-il plus facile ou plus difficile? Passez-vous plus de temps à développer ou moins?

Les optimisations sans profilage sont une perte de temps et d’argent.