Когда я должен опустить указатель кадра?
Есть ли существенная оптимизация при отсутствии указателя кадра?
Если я правильно понял, прочитав эту страницу, -fomit-frame-pointer
используется, когда мы хотим избежать сохранения, настройки и восстановления указателей кадров.
Это делается только для каждого вызова функции, и если да, действительно ли стоит избегать нескольких инструкций для каждой функции?
Разве это не тривиально для оптимизации.
Каковы фактические последствия использования этой опции помимо ограничений отладки?
Я скомпилировал следующий код C с этой опцией и без нее
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
'в двух файлах показан следующий код сборки:
--- 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
Может кто-то пролить свет на ключевые точки вышеприведенного кода, где -fomit-frame-pointer действительно сделал разницу?
Изменить: objdump
вывод заменен на gcc -S
Ответы
Ответ 1
-fomit-frame-pointer
позволяет использовать один дополнительный регистр для общего использования. Я бы предположил, что это действительно очень важно для 32-разрядной x86, которая немного голодает для регистров. *
Можно было бы ожидать, что EBP больше не будет сохраняться и не корректироваться при каждом вызове функции и, вероятно, некоторое дополнительное использование EBP в нормальном коде и меньше операций с стеком в случаях, когда EBP используется как регистр общего назначения.
Ваш код слишком прост, чтобы увидеть какую-либо выгоду от такого рода оптимизации - вы не используете достаточное количество регистров. Кроме того, вы не включили оптимизатор, который может потребоваться, чтобы увидеть некоторые из этих эффектов.
* Регистры ISA, а не регистры микроархитектуры.
Ответ 2
Единственный недостаток его исключения в том, что отладка намного сложнее.
Основной недостаток заключается в том, что существует один дополнительный регистр общего назначения, который может существенно повлиять на производительность. Очевидно, что этот дополнительный регистр используется только при необходимости (возможно, в вашей простой функции это не так); в некоторых функциях он имеет большее значение, чем в других.
Ответ 3
Вы можете часто получать более значимый код сборки из GCC с помощью аргумента -S
для вывода сборки:
$ gcc code.c -S -o withfp.s
$ gcc code.c -S -o withoutfp.s -fomit-frame-pointer
$ diff -u withfp.s withoutfp.s
GCC не заботится об адресе, поэтому мы можем сравнивать фактические инструкции, созданные напрямую. Для вашей функции листа это дает:
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 не генерирует код, чтобы нажимать указатель на стек, и это изменяет относительный адрес аргументов, переданных функции в стек.
Ответ 4
Профилируйте свою программу, чтобы увидеть, есть ли существенная разница.
Далее, пропишите свой процесс разработки. Отладка легче или сложнее? Вы тратите больше времени на разработку или меньше?
Оптимизация без профилирования - пустая трата времени и денег.