Ассемблерные вставки

Тема в разделе "WASM.BEGINNERS", создана пользователем Kost, 14 дек 2005.

  1. Kost

    Kost New Member

    Публикаций:
    0
    Регистрация:
    11 дек 2005
    Сообщения:
    5
    Адрес:
    Russia
    У меня тут назрел один вопрос касательно ассемблерныых вставок в C++ Builder'e и в Delphi. Ведь теоретически на асме код должен выполнятся быстрее, я тут сделал небольшой эксперимент (см. аттач) и у меня получилось все наоборот, код на асме выполняется медленнее. Одна и та же операция на асме делается примерно на 20мс. медленее. Мож кто глянет, просто интересно для общего развития почему так происходит? Или я неправильно написал ассемблерный код?

    [​IMG] 1394665246__test.rar
     
  2. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Посмотри в дизассемблере код сравниваемых процедур, думаю разницу увидишь. Т.е. вряд ли цикл, который сделал компилятор, похоже на цикл, который сделал ты.
     
  3. Sl4v4

    Sl4v4 New Member

    Публикаций:
    0
    Регистрация:
    18 окт 2005
    Сообщения:
    69


    Не то что бы не правильно, но не совсем оптимально. по крайней мере компилятор это сделал лучше =)) Перепеши вставку так:
    Код (Text):
    1.  
    2. asm {
    3.  xor eax, eax
    4.  label:
    5.  inc eax
    6.  cmp eax,10000000
    7.  JL label
    8.  mov a, eax
    9. }
    10.  


    и посмотри на результаты. Операции с регистрами проходят гораздо быстрее чем с переменными.
     
  4. Kost

    Kost New Member

    Публикаций:
    0
    Регистрация:
    11 дек 2005
    Сообщения:
    5
    Адрес:
    Russia
    Я тут тоже подумал, что с регистрами процессор работать будет быстрее и написал такую вставку



    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

    }



    Это в общем тоже не помогло. Не знаю в чем проблема, но прям парадокс какой-то :)
     
  5. Bioramaenator

    Bioramaenator New Member

    Публикаций:
    0
    Регистрация:
    10 дек 2005
    Сообщения:
    3
    Неужели ты действительно думаешь что напишешь на асме код, лучше чем это сделает оптимизирующий компилятор?

    Лично я сомневаюсь, имхо ассемблерные вставки не особо помогают при оптимизации
     
  6. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    Kost

    теоретически на асме код должен выполнятся быстрее, я тут сделал небольшой эксперимент ... и у меня получилось все наоборот ... интересно для общего развития почему так происходит?

    1) в измеряемый интервал вместе с ассемблерной вставкой попал код Label2->Caption=IntToStr(a);;

    2) твой способ измерения интервала не самый точный;

    3) ты измеряешь время выполнения тривиального кода, полагаю, что BCB сгенерировал из Си'шного кода код не хуже твоего.



    ps На форуме были темы по измерению времени выполнения кода.



    Bioramaenator

    Лично я сомневаюсь

    Зря.

    Надо уметь выбирать время (т.е. когда исчерпаны другие методы оптимизации) и место (т.е. когда ты в состоянии написать код лучше "оптимизирующего компилятора") для ассемблерных вставок.
     
  7. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    0040239E - адрес метки, куда совершается jmp, не кратен даже 4, не говоря уже о 16 или 32. Попробуй вставить соответствующее количество nop'ов перед меткой label, чтобы выравнять её адрес.
     
  8. Sl4v4

    Sl4v4 New Member

    Публикаций:
    0
    Регистрация:
    18 окт 2005
    Сообщения:
    69
    Kost

    Ты смотрел в дизассемблере как это делает компилятор? Билдера у меня нет, так что я перепесал твой листинг под Delphi 7. Цикл там представлен в точности так, как показал я, с той лишь разницей, что там используется регистр ebx. Поскольку Делфи и Билдер близнецы-братья, думаю эту задачу они решают одинаково. Надо ли говорить, что после изменения исходника, циклы у меня выполнялись за одинаковое время? (15-16мс) Почему у тебя время разное мне непонятно, ведь код получается идентичный.
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Мало того, что адрес не выравнен - он еще находится вблизи 16-байтной границы => разбивает инструкцию (add или cmp) и в P6 и атлонах требует загрузки двух блоков декодирования вместо одного и как следствие +1 тик на каждый цикл (на P4 этого может не быть)



    Но очевидная неоптимальность - в зависимости инструкций inc и cmp, что приводит к чистой потере одного тика на каждый цикл и в итоге имеем 10^7 лишних тиков, что и выливается в десяток-другой миллисекунд. По правильному нужно использовать два регистра и независимые операции суммирования и подсчета циклов:
    Код (Text):
    1. mov eax,a
    2. mov ecx,10000000
    3. sub ecx,eax
    4. loop:
    5.   inc eax    ;а лучше add eax,1
    6.   sub ecx,1
    7.   jg _loop
     
  10. Kost

    Kost New Member

    Публикаций:
    0
    Регистрация:
    11 дек 2005
    Сообщения:
    5
    Адрес:
    Russia
    Sl4v4

    Я думаю что ты не видишь разницы потому что у тебя проц быстрее моего (у меня 3-й пень 1Гц). У меня код выполняется за 80мс сишный и за 100мс ассемблерный.
     
  11. Sl4v4

    Sl4v4 New Member

    Публикаций:
    0
    Регистрация:
    18 окт 2005
    Сообщения:
    69
    Первоначальный цикл выполнялся за ~16 и ~36 миллисекунд соответственно. Те же самые 20мс разницы. Проц действительно помощьнее - Athlon XP 2000+ (разогнан с 1700+). Но как быть с этим:

    Оптимизированный цикл while:
    Код (Text):
    1. CODE:0044EEA7 loc_44EEA7:                           ; CODE XREF: sub_44EDF0+BEj
    2. CODE:0044EEA7                 inc     ebx
    3. CODE:0044EEA8                 cmp     ebx, 989680h
    4. CODE:0044EEAE                 jl      short loc_44EEA7


    и ассемблерная вставка:
    Код (Text):
    1. CODE:0044ECF4 loc_44ECF4:                             ; CODE XREF: sub_44EC40+BAj
    2. CODE:0044ECF4                 inc     eax
    3. CODE:0044ECF5                 cmp     eax, 989680h
    4. CODE:0044ECFA                 jl      short loc_44ECF4
     
  12. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    1) 16 мс это разрешающая способность виндового таймера. Если увеличить число проходов цикла в 10 раз, то разницы практически не будет (при соблюдении условия 2)



    2) Сравнивать нужно при одинаковом окружении и одинаковом выравнивании метки цикла. Поскольку код получается практически идентичным, то можно просто последовательно записать два варианта и отключать один из них либо условной компиляцией либо просто закомментировать



    3) Для надежности нужно запускать тест несколько раз, т.к. винда может случайно вклиниваться в неподходящий момент и искажать результат



    При этих условия ес-но никакой разницы не будет



    А вообще, как я уже упоминал, в PIII, Pentium M и атолонах неудачное выравивание (точнее не-выравнивание ;)метки может давать разницу по кр.мере в 1 тик. Соответсвенно зная частоту проца и число повторений цикла можем определить разницу в миллисекундах: при 10^7 повторах потеря одного тика даст в итоге 10 мс при частоте 1ГГц
     
  13. Kost

    Kost New Member

    Публикаций:
    0
    Регистрация:
    11 дек 2005
    Сообщения:
    5
    Адрес:
    Russia
    Я блин туплю по страшному, но я попробовал переписать это все на Delphi и не компилируется ни фига. Вставка выглядит так:
    Код (Text):
    1.  
    2. asm
    3.   xor eax, eax
    4.   label:
    5.   inc eax
    6.   cmp eax, 10000000
    7.   JL label
    8.   mov a, eax
    9. end;
    10.  




    При компиляции пишет: Undeclared identifier: 'label'.

    Если делать такую вставку:


    Код (Text):
    1.  
    2. asm
    3.   mov eax,a
    4.   mov ecx,10000000
    5.   sub ecx,eax
    6.   loop:
    7.   inc eax
    8.   sub ecx,1
    9.   jg _loop
    10. end;
    11.  




    Пишет: Inline assembler syntax error и Undeclared identifier: 'loop'.

    В чем проблема не подскажете?

    Кстати на Delphi код выполнился быстрее чем на C++ Builder'e, примерно за 30мс.
     
  14. Sl4v4

    Sl4v4 New Member

    Публикаций:
    0
    Регистрация:
    18 окт 2005
    Сообщения:
    69


    Вот уж не думал



    "label", как и "loop" - это служебные слова используемые делфи. Их нельзя использовать как имена переменных и меток. Метки, как и переменные, надо сначала объявлять в секции "label". Имя метки, установленной в коде должно соответствовать имени метки на которую осуществляется переход (подчёркивание убери).
     
  15. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    В дельфийском асме можно использовать локальные метки, начинающиеся с символа @. В этом случае их специально объявлять не нужно и действуют они только в пределах блока asm..end.

    Используй @loop: ... jl @loop и все будет нормально



    PS: Приведенный мной вариант с независимыми операциями инкремента переменной (inc eax) и декремента счетчика (sub ecx,1) не дает выигрыша на PIII, т.к. в семействе P6 минимальная задержка цикла определяется jcc и составляет 2 тика. А вот на P4 это экономит полтакта (inc и sub выполняются параллельно за 0.5 такта)
     
  16. Kost

    Kost New Member

    Публикаций:
    0
    Регистрация:
    11 дек 2005
    Сообщения:
    5
    Адрес:
    Russia


    Спасибо, такая конструкция работает.



    Sl4v4

    Я кстати и раньше замечал, что идентичные проги написанные на Delphi и С++ Builder'e, на Delphi выполняются быстрее. Я как то делал курсовую на Delphi (программа должна строить график функции с определенной точностью), а потом переписал на C++, и сишная прога считала координаты точек заметно медленее.