netch: (Default)
[personal profile] netch
Одно из наиболее странных свойств gcc - оптимизация по принципу "или всё, или ничего". Чтобы пользоваться отладчиком, оптимизацию надо выключить. Но при этом он начинает генерировать такой код, что хоть святых выноси - восемь mov на одно значение вместо одного, сохранение значения из регистра в стек с тем чтобы тут же прочитать его из стека в регистр, и тому подобные ужасы. Чтобы получить более-менее нормальный код, надо дать хотя бы -O, но тогда начнётся смещение выполняемых действий относительно меток строк и пошаговая отладка станет невозможной. Ладно, для свежеиспечённого изделия такое понятно, но ведь gcc скоро стукнет 20 лет. Неужели за это время никто не озаботился проблемой качества кода на нижних уровнях оптимизации? Любой коммерческий компилятор (даже Borland'овский) справляется с этим значительно лучше 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 нормальную промежуточную оптимизацию, действующую в пределах одной строки? И стоит ли оно возни? Смотря на коммерческие компиляторы я думаю, что стоит. И в чём загвоздка?

Date: 2006-01-22 02:38 pm (UTC)
From: (Anonymous)
"в пределах одной строки" - это как?

В данном случае, как раз проблема в том, что компилятор вперед не заглядывает, и прошлого тоже не помнит.

Date: 2006-01-22 06:58 pm (UTC)
From: (Anonymous)
Строка - не синтаксический элемент. Насколько я помню, в C все пробельные символы равноправны.

Просто, насколько я понимаю, в GCC оптимизация происходит очень поздно, когда от строк осталось уже одно воспоминание.

Date: 2006-01-22 02:43 pm (UTC)
From: [identity profile] dinozavrik.livejournal.com
А как с этим у MSVC?

Date: 2006-01-23 09:12 am (UTC)
From: (Anonymous)
Последний MSVC 2005 - его и проверяй - или 2003 в крайнем случае
6.0 - это старо как мир :)

Date: 2006-01-29 09:32 pm (UTC)
From: [identity profile] noser.livejournal.com
Автор этих строк. Я там явно пересолил с дороговизной пушей/попов. Она есть, но не такая страшная, на самом деле она минимальная. Собственно, меня больше возмущает неоправданный расход целых двух регистров на стек.

Сейчас лень лезть в optimization guide. Насколько я помню, в П4, архитектуру которого я знаю лучше чем i686, PUSH раскладывается в два микроопа - ALU: двинуть ESP и MEMSTORE: засунуть значение в память. В случае с MOV-ом, имеем только второй.

Фишка в том, что у P4 есть два ALU, каждый из которых может выполнять две ALU инструкции за такт, параллельно с юнитом MEMSTORE, который может выполнить одно засовывание за такт. В результате несколько последовательных пушей загружают исполняющие юниты след. образом:

такт ALU  MEMSTORE
1    0.5  1
2    0.5  1
3    0.5  1
...

Имеем по одному такту на PUSH - столько же, сколько и MOV, который загружает только MEMSTORE. Благодаря оптимизации архитектуры, кривизна PUSH не наносит урона :) Ну разве что гипертрединг тормозит, да процессор греется сильнее.

Date: 2006-01-29 09:56 pm (UTC)
From: [identity profile] noser.livejournal.com
А если ему сказать -fomit-frame-pointer?

Date: 2006-01-29 10:10 pm (UTC)
From: [identity profile] noser.livejournal.com
Политика понятная, по-моему: много push-ей подряд выполняется за примерно то же время, что и много mov-ов. На гипертрединг забиваем.

Date: 2006-01-29 09:41 pm (UTC)
From: [identity profile] noser.livejournal.com
Да, и надо отметить, что отдельностоящий PUSH имеет латентность 1.5 такта, в отличие от отдельностоящего MOV в память.

Date: 2006-01-22 02:56 pm (UTC)
From: [identity profile] beldmit.livejournal.com
Черт его знает. В ассемблере никогда ничего не понимал, а код, собранный с -O2, регулярно доводилось, тупо втыкаясь в экран, отлаживать. Кроме этой самой тупости, никаких проблем.

Date: 2006-01-22 03:51 pm (UTC)
From: [identity profile] beldmit.livejournal.com
Я проверял. Действительно хуже, чем Intel. Где-то у меня в ЖЖ и цифры были.

Консольный gdb при -O2 у меня много раз показывает одну и ту же строчку, пока, наконец, не проинициализирует все переменные. Но при этом честно пишет номера строк, и видно, что они немонотонны.

Date: 2006-01-22 04:00 pm (UTC)
From: (Anonymous)
gcc 2.95 был более вменяемым?

main:
        pushl %ebp
        movl %esp,%ebp
        subl $24,%esp
        xorl %eax,%eax
        jmp .L2
        .p2align 4,,7
.L2:
        leave
        ret
        .size   main , . - main


% gcc -v
Reading specs from /usr/lib/gcc-lib/i386-unknown-openbsd3.5/2.95.3/specs
gcc version 2.95.3 20010125 (prerelease, propolice)

Date: 2006-01-22 06:34 pm (UTC)
singalen: (Default)
From: [personal profile] singalen
По доке для 3.3.3,
-O turns on the following optimization flags:
          -fdefer-pop 
          -fmerge-constants 
          -fthread-jumps 
          -floop-optimize 
          -fcrossjumping 
          -fif-conversion 
          -fif-conversion2 
          -fdelayed-branch 
          -fguess-branch-probability 
          -fcprop-registers

-O also turns on -fomit-frame-pointer on machines where doing so does not interfere with debugging.

Видимо, тебе нужно подмножество этого набора. Может, без послених пяти или типа того.

Date: 2006-01-23 08:09 am (UTC)
singalen: (Default)
From: [personal profile] singalen
Ещё, если мне не изменяет, есть -fdisable-блаблабла.
Посмотреть по доке, какой набор опций включает -O на 3.3.4, и попробовать их вырубать по одной.
Если не поможет, пробовать методом дихотомии вырубать множество оставшихся.

Date: 2006-01-23 09:22 am (UTC)
singalen: (Default)
From: [personal profile] singalen
Я неясно выразился. Дихотомией можно исследовать множество остальных опций, либо, что то же самое, вообще всех опций оптимизации. Так тоже можно вычленить нужную, вернее, ненужную опцию.

Date: 2006-01-23 09:49 am (UTC)
singalen: (Default)
From: [personal profile] singalen
Я был лучшего мнения о gcc 8(
Видимо, даже самые лучшие опенсорсный продукт неизбежно накрывает ползучим фичеризмом и поганым кодом.
С третьей стороны, ты можешь сделать патч или хотя бы засабмитить баг :) Правда, он наверняка пойдёт в Duplicate.

Date: 2006-01-23 11:28 am (UTC)
singalen: (Default)
From: [personal profile] singalen
Польза очевидная - пропадёт проблема, проявляющаяся в твоём случае. Главное её убедительно описать.

Turbo Pascal

Date: 2006-01-23 09:10 am (UTC)
From: (Anonymous)
Хочешь построчную компиляцию - юзай Turbo Pascal or Delphi

joss

Re: Turbo Pascal

Date: 2006-01-24 08:58 am (UTC)
From: [identity profile] jossik.livejournal.com
Привет.
В Watcom при включении отладки оптимизация отваливается - это раз, в gcc это так же.
Если надо отладка и оптимизация - то разбей код на две группы : одну компиль
с оптимизацией, другую с отладкой - постепенно уменьшай 2-ю.
И ввобще - лучшая оптимизация - это лучший алгоритм :-P
Или новый процессор :)

Date: 2006-01-29 10:30 pm (UTC)
From: [identity profile] noser.livejournal.com
Видимо, с выключенной оптимизацией у gcc оптимизируется время компиляции :) Не, серьезно.

Profile

netch: (Default)
netch

December 2023

S M T W T F S
     12
3456789
10111213141516
171819 20212223
24252627282930
31      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 9th, 2026 02:16 pm
Powered by Dreamwidth Studios