gcc и оптимизация
Jan. 22nd, 2006 03:25 pmОдно из наиболее странных свойств gcc - оптимизация по принципу "или всё, или ничего". Чтобы пользоваться отладчиком, оптимизацию надо выключить. Но при этом он начинает генерировать такой код, что хоть святых выноси - восемь mov на одно значение вместо одного, сохранение значения из регистра в стек с тем чтобы тут же прочитать его из стека в регистр, и тому подобные ужасы. Чтобы получить более-менее нормальный код, надо дать хотя бы -O, но тогда начнётся смещение выполняемых действий относительно меток строк и пошаговая отладка станет невозможной. Ладно, для свежеиспечённого изделия такое понятно, но ведь gcc скоро стукнет 20 лет. Неужели за это время никто не озаботился проблемой качества кода на нижних уровнях оптимизации? Любой коммерческий компилятор (даже Borland'овский) справляется с этим значительно лучше gcc.
А самый злобный пример на его недооптимизацию выглядит так. Возьмём простейшую main():
Сассемблируем (результат одинаков для 3.4, 4.0, 4.1):
Что это за кошмар с вычислениями, которые можно было сделать на этапе компиляции?? Ах, это -mpreferred-stack-boundary=4... При подъёме до -O код становится вменяемым:
Только не надо говорить, что отладчик - сакс. Луговского слышали, всё понятно. Отладчик - рулез, если использовать его по назначению. Например, по срабатыванию breakpoint'а двигаться дальше сделав отладочную печать, или проверять условие и при его невыполнении возобновлять выполнение. (Второе не является watchpoint, по крайней мере в смысле gdb.) Есть ещё много вариантов, которые не сводятся к тупому втыканию в экран. Хотя и оно иногда полезно - чтобы наткнуться на проблему которую не замечал в упор из-за "замыленного глаза".
Насколько сложно было бы сделать в gcc нормальную промежуточную оптимизацию, действующую в пределах одной строки? И стоит ли оно возни? Смотря на коммерческие компиляторы я думаю, что стоит. И в чём загвоздка?
А самый злобный пример на его недооптимизацию выглядит так. Возьмём простейшую main():
int main()
{
int n, a, b;
return 0;
}
Сассемблируем (результат одинаков для 3.4, 4.0, 4.1):
main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
andl $-16, %esp
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
subl %eax, %esp
movl $0, %eax
leave
ret
Что это за кошмар с вычислениями, которые можно было сделать на этапе компиляции?? Ах, это -mpreferred-stack-boundary=4... При подъёме до -O код становится вменяемым:
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
subl $16, %esp
movl $0, %eax
leave
ret
Только не надо говорить, что отладчик - сакс. Луговского слышали, всё понятно. Отладчик - рулез, если использовать его по назначению. Например, по срабатыванию breakpoint'а двигаться дальше сделав отладочную печать, или проверять условие и при его невыполнении возобновлять выполнение. (Второе не является watchpoint, по крайней мере в смысле gdb.) Есть ещё много вариантов, которые не сводятся к тупому втыканию в экран. Хотя и оно иногда полезно - чтобы наткнуться на проблему которую не замечал в упор из-за "замыленного глаза".
Насколько сложно было бы сделать в gcc нормальную промежуточную оптимизацию, действующую в пределах одной строки? И стоит ли оно возни? Смотря на коммерческие компиляторы я думаю, что стоит. И в чём загвоздка?
no subject
Date: 2006-01-29 09:45 pm (UTC)void qq( const char* f, const char* a1, const char* a2 )
{
printf( f, a1, a2 );
mmap(0, 1, 2, 3, 4, 5);
}
Проверяем через icc 9.0.
Тест 1:
$ icc -S s5.c -O0 -march=pentium4
В выводе:
..B1.1: # Preds ..B1.0
pushl %ebp #2.1
movl %esp, %ebp #2.1
addl $-12, %esp #3.3
movl 8(%ebp), %eax #3.11
movl %eax, (%esp) #3.11
movl 12(%ebp), %eax #3.14
movl %eax, 4(%esp) #3.14
movl 16(%ebp), %eax #3.18
movl %eax, 8(%esp) #3.18
call printf #3.3
Тест 2:
$ icc -S s5.c -O3 -march=pentium4
В выводе:
..B1.1: # Preds ..B1.0
pushl 12(%esp) #2.1
pushl 12(%esp) #2.1
pushl 12(%esp) #2.1
call printf #3.3
Моя совсем перестала понимать политику партии.
no subject
Date: 2006-01-29 09:56 pm (UTC)no subject
Date: 2006-01-29 10:03 pm (UTC)no subject
Date: 2006-01-29 10:10 pm (UTC)