Война миров - ассемблер против си

Программа сократилась до 20h байт,


Ага! Программа сократилась до 20h байт, что всего на 8 байт длиннее ассемблерной программы, но цикл практически не изменился. По прежнему используется сложная адресация и никому не нужная команда MOVZX. Изменилась только точка входа в цикл. Вместо того, чтобы проверять значение аргумента n _до_ входа в цикл, компилятор сформировал условный переход на проверку, выполняемую перед выходом из цикла, то есть использовал тот же самый трюк, что и мы в нашей ассемблерной программе, однако, в отличи от нас, компилятор использовал 4е регистра, а не 3 и к тому же сгенерировал стандартный пролог/эпилог, который, впрочем, при желании можно подавить ключами командной строки, но это все равно не поможет, поскольку…

Цикл остается неразвернутым! (под "разворотом" в общем случае понимается многократное дублирование цикла) На таком крохотном "пяточке" процессору просто негде развернуться, поэтому все будет очень жутко тормозить – как при ручной оптимизации, так и при машинной. Компилятор MS VC вообще не умеет разворачивать циклы. По жизни. GCC умеет, но по умолчанию не делает этого даже на уровне оптимизации ?O3, разве что его специального попросить, указав ключ -funroll-all-loops

в командной строке. Циклы с известным количеством итераций, где const <= 32 разворачиваются полностью, при const >
 32 — на ~4x (точное значение зависит от количества инструкций в теле цикла), но циклы с неизвестным количеством итераций (то есть такие циклы, параметр которых — переменная, а не константа) не разворачиваются вообще! И в этом есть свой резон.

При малых количествах итераций цикл лучше не разворачивать, поскольку выигрыш в скорости не окупится увеличением размера программы и усложнением логики, особенно, если цикл расположен внутри редко вызываемой функции. А вот разворот часто вызываемого цикла с большим количеством итераций дает по меньшей мере двукратный прирост производительности. Главное — не переборщить и не развернуть цикл сильнее, чем требуется. На большинстве процессоров рост скорости прекращается при развороте на 8 итераций, и дальнейшее дублирование тела цикла лишь увеличивает его размер (см. рис. 3).




Содержание раздела