_DEN_ MS VC++ любит менять инструкции местами, т.к. в большинстве случаев это даёт выйгрыш по скорости, иногда экономит регистры. Если учесть что суперскаляр всё равно потом перемешает эти инструкции и реальную последовательность выполнения предсказать трудно, то винить MS VC++ можно разве что в обфускации кода
_DEN_ Ну уж не знаю - ты бы хоть пример привел. Что значит "Не, не левый, осмысленный" ? Несет он полезную нагрузку или нет - или это "бестолковый" паддинг или задержка ? Может это обычная реализация условного mov Или может оптимизация под i486 ? cresta Если речь идет о jcc в конце лупа, то в P6 (да наверное и в Атолонах - где декодирование идет на лету) бывают не очень приятные ситуации, когда команда перехода разбивается границей блока декодирования или идет первой в блоке - это может откушать 1-2 тика, тогда нужно попытаться ужаться и уместить ее в блок, а если не получается, то сместить дальше - в следующий. В чем может быть смысл задержки jcc по времени - я себе не представляю
leo Если судить по RDTSC, то на PIII я примерно догадываюсь почему код справа обгоняет в 2 раза левого, а на P4 ситуация с точность до наоборот, но факт влияния mov очевиден для обоих, причем без роли предсказания или смещения jxx Код (Text): mov eax,[x] cmp ecx,[x] mov eax,[x] mov eax,[x] cmp ecx,[x] mov eax,[x] jz @f jz @f @@: @@: S_T_A_S_ Скорее всего, у Фога даже есть такой пример по устранению задержки AGI
Попробую показать, что я имел ввиду под ожиданием выполнения и перегруппировкой мопов по портам: 1,2,3,4 - такты, p0,p1,p2 - порты исполнения PIII Код (Text): ;=================================================================== ; | p0 | p1 | p2 | ;========================================= ; 1 | | | mov eax,[x] | mov eax,[x] ;========================================= mov eax,[x] ; 2 | | | mov eax,[x] | cmp ecx,[x] ;========================================= jz @f ; 3 | | | mov tmp,[x] | @@: ;========================================= ; 4 | cmp ecx,tmp | jz @f | | ;=================================================================== ;=================================================================== ; | p0 | p1 | p2 | ;========================================= ; 1 | | | mov tmp,[x] | cmp ecx,[x] ;========================================= mov eax,[x] ; 2 | cmp ecx,tmp | | mov eax,[x] | mov eax,[x] ;========================================= jz @f ; 3 | | jz @f | mov eax,[x] | @@: ;===================================================================
bogrus Ес-но, второй вариант лучше. А на PIII к тому же и декодирование идет быстрее (если начало кода выравнено на 16). Во втором случае имеем классическую схему за 2 такта, а в первом случае минимум 3 такта - в первом такте 2 мува, во втором 2-х моповый cmp и конец блока декодирования, значит jcc может декодироваться только в 3-м такте, если префетч не задержится. Ясно, что примеры "полезности mov перед jcc" можно придумать. Но все-таки хотелось бы посмотреть на живые примерчики - чего там компиляторы оптимизируют
Еще тут пара интересных моментов. Код (Text): mov ecx, dword [eax] push ecx Регистр ecx далее не юзается. Почему компилер не поставил вместо этого просто Код (Text): push dword [eax] ??? И еще, почему паддинг делается всякими извратами типа Код (Text): lea esp, [esp] Почему просто нопами не пропаддить?
_DEN_ Таких моментов может быть много (например условные переходы, которые никогда не выполнятся). Если нет ключей оптимизации, компилятор откровенно халявничает без ключей. Но с ключами всё преображается, и придраться не к чему. Попробуй с разными ключами оптимизации, наверняка mov ecx, dword [eax] push ecx изчезнет. cl /Ox например lea esp, [esp] это почти стандарт , или lea ecx[ecx], многие вставляют именно такой фрагмент для выравнивания. А нопы например я ни у одного компилятора не видел.
Я говорю о том, что весь интервал до указанного align не забивается одними только нопами. А весь не забивается вероятно потому, что lea esp,[esp] быстрее кучи нопов на старых процах, поэтому его и ставят. Это только предположение.
> "Вот почему все нопами не паддится?" > "lea esp,[esp] быстрее кучи нопов на старых процах" Потому, что это быстрее на всех процах, и в руководствах по оптимизации как Intel, так и AMD, рекомендуют заменять цепочки нопов простыми одномоповыми (direct path) операциями соотвествующего размера, т.к. это как минимум увеличивает пропускную способность декодера (на P4 доставку мопов из трайс-кэша), а в P6\P4 еще и время исполнения (на атлонах нопы не исполняются). В данном сл. lea esp,[esp] простая инструкция, заменяющая 3 нопа, поэтому вместе с ней за один такт могут декодироваться еще две простые инструкции. Это же относится и к исполнению на P6\P4, например, на P6 нопы могут выполняться по 2 за такт, поэтому за 2 такта можно выполнить 3 нопа + 1 доп. ALU инструкцию или 1 lea и 3 доп.инструкции. А вот кстати для 4 байт можно было бы использовать lea esp,[esp+0], но все компилеры почему-то этого не любят и выкидывают нулевые смещения С пушами дело обстоит не так очевидно. На атлоне push m32 - vector path и вместе с ней не могут быть декодированы другие инструкции, а mov r32,m32 и push r32 - direct path и в одном такте с ними может пролезть еще одна direct path. Для P4 в режиме доставки мопов из трейс-кэша без разницы, а в режиме декодирования mov кушает доп.такт. В P6 если mov влезает в предыдущую торойку декодирования, то без разницы, если нет - то тоже потеря такта. Так что фиг знает, что лучше. Возможно тут еще скрытое выравнивание последующего кода может быть задействовано
> "fasm вставляет nop'ы" Ленится Хотя отличие NOP от прочих филлеров в том, что это спец.операция: на атлонах она вообще не выполняется, а у интелов устранена зависимость по данным в регистре eax. А вот прочие lea и т.п. это обычные операции и в общем сл. имеют зависимость по данным соотв. регистра. Поэтому тут важно не сглупить и не создать ненужную зависмость (типа div ecx + lea edx,[edx]) PS: А вот выкидывать 0 из lea r32,[r32+0] фасму не лень, хотя если я пишу 0, значит он мне для чего то нужен и нечего оптимизировать, когда не просят