Есть ли какая-либо существенная оптимизация при исключении указателя кадра? Если я понял правильно, читая эту страницу, -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
-fomit-frame-pointer
позволяет одному дополнительному регистру быть доступным для общего использования. Я предполагаю, что это действительно имеет значение только для 32-разрядной архитектуры x86, которой немного не хватает регистров. *
Можно было бы ожидать, что EBP больше не сохраняется и не корректируется при каждом вызове функции, и, возможно, некоторое дополнительное использование EBP в нормальном коде и меньшее количество операций со стеком в тех случаях, когда EBP используется как регистр общего назначения.
Ваш код слишком прост, чтобы увидеть какую-либо пользу от такого рода оптимизации - вы используете недостаточно регистров. Кроме того, вы не включили оптимизатор, который может потребоваться, чтобы увидеть некоторые из этих эффектов.
* Регистры ISA, а не регистры микроархитектуры.
Единственным недостатком его исключения является то, что отладка намного сложнее.
Основным положительным моментом является наличие одного дополнительного регистра общего назначения, который может иметь большое значение для производительности. Очевидно, этот дополнительный регистр используется только при необходимости (вероятно, в вашей очень простой функции это не так); в некоторых функциях это имеет большее значение, чем в других.
Вы часто можете получить более понятный код сборки из 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 не генерирует код для вставки указателя кадра в стек, и это изменяет относительный адрес аргументов, переданных функции в стеке.
Профилируйте свою программу, чтобы увидеть, есть ли существенная разница.
Затем опишите свой процесс разработки. Отладка проще или сложнее? Вы тратите больше времени на разработку или меньше?
Оптимизация без профилирования - пустая трата времени и денег.