У меня тут назрел один вопрос касательно ассемблерныых вставок в C++ Builder'e и в Delphi. Ведь теоретически на асме код должен выполнятся быстрее, я тут сделал небольшой эксперимент (см. аттач) и у меня получилось все наоборот, код на асме выполняется медленнее. Одна и та же операция на асме делается примерно на 20мс. медленее. Мож кто глянет, просто интересно для общего развития почему так происходит? Или я неправильно написал ассемблерный код? 1394665246__test.rar
Посмотри в дизассемблере код сравниваемых процедур, думаю разницу увидишь. Т.е. вряд ли цикл, который сделал компилятор, похоже на цикл, который сделал ты.
Не то что бы не правильно, но не совсем оптимально. по крайней мере компилятор это сделал лучше =)) Перепеши вставку так: Код (Text): asm { xor eax, eax label: inc eax cmp eax,10000000 JL label mov a, eax } и посмотри на результаты. Операции с регистрами проходят гораздо быстрее чем с переменными.
Я тут тоже подумал, что с регистрами процессор работать будет быстрее и написал такую вставку asm { mov eax, a label: add eax,1 cmp eax,10000000 JL label mov a, eax } в отладчике (OllyDbg) этот фрагмент выглядел так: 0040239E |> 83C0 01 /ADD EAX,1 004023A1 |. 3D 80969800 |CMP EAX,989680 004023A6 |.^7C F6 \JL SHORT Project1.0040239E на скорости это никак не отразилось. Пробовал, по совету, и такую вставку: asm { xor eax, eax label: inc eax cmp eax,10000000 JL label mov a, eax } Это в общем тоже не помогло. Не знаю в чем проблема, но прям парадокс какой-то
Неужели ты действительно думаешь что напишешь на асме код, лучше чем это сделает оптимизирующий компилятор? Лично я сомневаюсь, имхо ассемблерные вставки не особо помогают при оптимизации
Kost теоретически на асме код должен выполнятся быстрее, я тут сделал небольшой эксперимент ... и у меня получилось все наоборот ... интересно для общего развития почему так происходит? 1) в измеряемый интервал вместе с ассемблерной вставкой попал код Label2->Caption=IntToStr(a);; 2) твой способ измерения интервала не самый точный; 3) ты измеряешь время выполнения тривиального кода, полагаю, что BCB сгенерировал из Си'шного кода код не хуже твоего. ps На форуме были темы по измерению времени выполнения кода. Bioramaenator Лично я сомневаюсь Зря. Надо уметь выбирать время (т.е. когда исчерпаны другие методы оптимизации) и место (т.е. когда ты в состоянии написать код лучше "оптимизирующего компилятора") для ассемблерных вставок.
0040239E - адрес метки, куда совершается jmp, не кратен даже 4, не говоря уже о 16 или 32. Попробуй вставить соответствующее количество nop'ов перед меткой label, чтобы выравнять её адрес.
Kost Ты смотрел в дизассемблере как это делает компилятор? Билдера у меня нет, так что я перепесал твой листинг под Delphi 7. Цикл там представлен в точности так, как показал я, с той лишь разницей, что там используется регистр ebx. Поскольку Делфи и Билдер близнецы-братья, думаю эту задачу они решают одинаково. Надо ли говорить, что после изменения исходника, циклы у меня выполнялись за одинаковое время? (15-16мс) Почему у тебя время разное мне непонятно, ведь код получается идентичный.
Мало того, что адрес не выравнен - он еще находится вблизи 16-байтной границы => разбивает инструкцию (add или cmp) и в P6 и атлонах требует загрузки двух блоков декодирования вместо одного и как следствие +1 тик на каждый цикл (на P4 этого может не быть) Но очевидная неоптимальность - в зависимости инструкций inc и cmp, что приводит к чистой потере одного тика на каждый цикл и в итоге имеем 10^7 лишних тиков, что и выливается в десяток-другой миллисекунд. По правильному нужно использовать два регистра и независимые операции суммирования и подсчета циклов: Код (Text): mov eax,a mov ecx,10000000 sub ecx,eax loop: inc eax ;а лучше add eax,1 sub ecx,1 jg _loop
Sl4v4 Я думаю что ты не видишь разницы потому что у тебя проц быстрее моего (у меня 3-й пень 1Гц). У меня код выполняется за 80мс сишный и за 100мс ассемблерный.
Первоначальный цикл выполнялся за ~16 и ~36 миллисекунд соответственно. Те же самые 20мс разницы. Проц действительно помощьнее - Athlon XP 2000+ (разогнан с 1700+). Но как быть с этим: Оптимизированный цикл while: Код (Text): CODE:0044EEA7 loc_44EEA7: ; CODE XREF: sub_44EDF0+BEj CODE:0044EEA7 inc ebx CODE:0044EEA8 cmp ebx, 989680h CODE:0044EEAE jl short loc_44EEA7 и ассемблерная вставка: Код (Text): CODE:0044ECF4 loc_44ECF4: ; CODE XREF: sub_44EC40+BAj CODE:0044ECF4 inc eax CODE:0044ECF5 cmp eax, 989680h CODE:0044ECFA jl short loc_44ECF4
1) 16 мс это разрешающая способность виндового таймера. Если увеличить число проходов цикла в 10 раз, то разницы практически не будет (при соблюдении условия 2) 2) Сравнивать нужно при одинаковом окружении и одинаковом выравнивании метки цикла. Поскольку код получается практически идентичным, то можно просто последовательно записать два варианта и отключать один из них либо условной компиляцией либо просто закомментировать 3) Для надежности нужно запускать тест несколько раз, т.к. винда может случайно вклиниваться в неподходящий момент и искажать результат При этих условия ес-но никакой разницы не будет А вообще, как я уже упоминал, в PIII, Pentium M и атолонах неудачное выравивание (точнее не-выравнивание метки может давать разницу по кр.мере в 1 тик. Соответсвенно зная частоту проца и число повторений цикла можем определить разницу в миллисекундах: при 10^7 повторах потеря одного тика даст в итоге 10 мс при частоте 1ГГц
Я блин туплю по страшному, но я попробовал переписать это все на Delphi и не компилируется ни фига. Вставка выглядит так: Код (Text): asm xor eax, eax label: inc eax cmp eax, 10000000 JL label mov a, eax end; При компиляции пишет: Undeclared identifier: 'label'. Если делать такую вставку: Код (Text): asm mov eax,a mov ecx,10000000 sub ecx,eax loop: inc eax sub ecx,1 jg _loop end; Пишет: Inline assembler syntax error и Undeclared identifier: 'loop'. В чем проблема не подскажете? Кстати на Delphi код выполнился быстрее чем на C++ Builder'e, примерно за 30мс.
Вот уж не думал "label", как и "loop" - это служебные слова используемые делфи. Их нельзя использовать как имена переменных и меток. Метки, как и переменные, надо сначала объявлять в секции "label". Имя метки, установленной в коде должно соответствовать имени метки на которую осуществляется переход (подчёркивание убери).
В дельфийском асме можно использовать локальные метки, начинающиеся с символа @. В этом случае их специально объявлять не нужно и действуют они только в пределах блока asm..end. Используй @loop: ... jl @loop и все будет нормально PS: Приведенный мной вариант с независимыми операциями инкремента переменной (inc eax) и декремента счетчика (sub ecx,1) не дает выигрыша на PIII, т.к. в семействе P6 минимальная задержка цикла определяется jcc и составляет 2 тика. А вот на P4 это экономит полтакта (inc и sub выполняются параллельно за 0.5 такта)
Спасибо, такая конструкция работает. Sl4v4 Я кстати и раньше замечал, что идентичные проги написанные на Delphi и С++ Builder'e, на Delphi выполняются быстрее. Я как то делал курсовую на Delphi (программа должна строить график функции с определенной точностью), а потом переписал на C++, и сишная прога считала координаты точек заметно медленее.