QueryPerformanceCounter

Тема в разделе "WASM.ASSEMBLER", создана пользователем NetDemon, 6 июн 2007.

  1. NetDemon

    NetDemon New Member

    Публикаций:
    0
    Регистрация:
    24 апр 2007
    Сообщения:
    22
    Пытаюсь померить скорость,
    результат скачет в диком диапазоне, то ...e+300 то ...e-400
    не могу понять чем вызван такой разброс
    Код (Text):
    1.     invoke  GetCurrentProcess
    2.                 invoke  SetPriorityClass, eax, REALTIME_PRIORITY_CLASS
    3.                 invoke  GetCurrentThread
    4.             invoke  SetThreadPriority, eax, THREAD_PRIORITY_TIME_CRITICAL
    5.         invoke  Sleep, 100
    6.             ;тест  data_input
    7.             invoke SendMessage,hIDC_n_cicl,WM_GETTEXT, 15, addr str_for_float
    8.             invoke ustr2dw, addr str_for_float
    9.             mov Ncicl, eax
    10.             mov iCicl, eax
    11.             invoke QueryPerformanceFrequency, addr f
    12.             invoke QueryPerformanceCounter, addr c1
    13.             rdtsc
    14.             mov dword ptr [rez_data],eax
    15.             mov dword ptr [rez_data+4],edx
    16.             invoke Sleep,100
    17.             rdtsc
    18.             sub eax,dword ptr [rez_data]
    19.             sbb edx,dword ptr [rez_data+4]
    20.             mov dword ptr [rez_data],eax
    21.             mov dword ptr [rez_data+4],edx
    22.             ;----
    23.           ii: invoke data_input, pBuffNMPTH, addr hIDC_1_00, 16
    24.             dec [iCicl]
    25.             jnz ii
    26.             ;----
    27.             invoke QueryPerformanceCounter, addr c2
    28.             fld qword ptr [rez_data]
    29.             fld qword ptr [f]
    30.             fld qword ptr [c2]
    31.             fld qword ptr [c1]
    32.             fsub
    33.             fdiv
    34.             fmul
    35.             fstp qword ptr [rez_data]
    36.         ;тест собственно процедуры
    37.         invoke QueryPerformanceFrequency, addr f
    38.             invoke QueryPerformanceCounter, addr c1
    39.             rdtsc
    40.             mov dword ptr [rez],eax
    41.             mov dword ptr [rez],edx
    42.             invoke Sleep,100
    43.             rdtsc
    44.             sub eax,dword ptr [rez]
    45.             sbb edx,dword ptr [rez+4]
    46.             mov dword ptr [rez],eax
    47.             mov dword ptr [rez+4],edx
    48.         ;----
    49.           iii: invoke data_input, pBuffNMPTH, addr hIDC_1_00, 16
    50.             push pBuffNMPTH
    51.             call sample_1
    52.             dec [Ncicl]
    53.             jnz iii
    54.             ;----
    55.             invoke QueryPerformanceCounter, addr c2
    56.             fld qword ptr [rez]
    57.             fld qword ptr [f]
    58.             fld qword ptr [c2]
    59.             fld qword ptr [c1]
    60.             fsub
    61.             fdiv
    62.             fmul
    63.             fstp qword ptr [rez]
    64.             ;вычислим результат с учетом теста data_input
    65.             mov eax, dword ptr [rez]
    66.             mov edx, dword ptr [rez+4]
    67.             sub eax, dword ptr [rez_data]
    68.             sbb edx, dword ptr [rez_data+4]
    69.             mov dword ptr [rez], eax
    70.             mov dword ptr [rez+4], edx
    71.             ;выведем на экран данные теста
    72.             invoke FloatToStr, rez, addr str_for_float
    73.             invoke SendMessage,hIDC_test_rez,WM_SETTEXT, NULL, addr str_for_float
     
  2. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    QueryPerformanceXXX выдают результат в Int64, поэтому загружать f,c1,c2 нужно не fld, а fild (как целые)
    PS: И res_data тоже
     
  3. NetDemon

    NetDemon New Member

    Публикаций:
    0
    Регистрация:
    24 апр 2007
    Сообщения:
    22
    leo
    Изменил
    Код (Text):
    1.         invoke  GetCurrentProcess
    2.             invoke  SetPriorityClass, eax, REALTIME_PRIORITY_CLASS
    3.             invoke  GetCurrentThread
    4.             invoke  SetThreadPriority, eax, THREAD_PRIORITY_TIME_CRITICAL
    5.             invoke  Sleep, 100
    6.             ;тест  data_input c Ncicl
    7.             invoke SendMessage,hIDC_n_cicl,WM_GETTEXT, 15, addr str_for_float
    8.             invoke ustr2dw, addr str_for_float
    9.             mov Ncicl, eax
    10.             mov iCicl, eax
    11.             invoke QueryPerformanceFrequency, addr f
    12.             invoke QueryPerformanceCounter, addr c1
    13.             rdtsc
    14.             mov dword ptr [rez_data],eax
    15.             mov dword ptr [rez_data+4],edx
    16.             invoke Sleep,100
    17.             rdtsc
    18.             sub eax,dword ptr [rez_data]
    19.             sbb edx,dword ptr [rez_data+4]
    20.             mov dword ptr [rez_data],eax
    21.             mov dword ptr [rez_data+4],edx
    22.             ;----
    23.           ii: invoke data_input, pBuffNMPTH, addr hIDC_1_00, 16
    24.             dec [iCicl]
    25.             jnz ii
    26.             ;----
    27.             invoke QueryPerformanceCounter, addr c2
    28.             fild qword ptr [rez_data]
    29.             fild qword ptr [f]
    30.             fild qword ptr [c2]
    31.             fild qword ptr [c1]
    32.             fsub
    33.             fdiv
    34.             fmul
    35.             fistp qword ptr [rez_data]
    36.             ;тест собственно процедуры
    37.             invoke QueryPerformanceFrequency, addr f
    38.             invoke QueryPerformanceCounter, addr c1
    39.             rdtsc
    40.             mov dword ptr [rez],eax
    41.             mov dword ptr [rez],edx
    42.             invoke Sleep,100
    43.             rdtsc
    44.             sub eax,dword ptr [rez]
    45.             sbb edx,dword ptr [rez+4]
    46.             mov dword ptr [rez],eax
    47.             mov dword ptr [rez+4],edx
    48.             ;----
    49.           iii: invoke data_input, pBuffNMPTH, addr hIDC_1_00, 16
    50.             push pBuffNMPTH
    51.             call sample_1
    52.             dec [Ncicl]
    53.             jnz iii
    54.             ;----
    55.             invoke QueryPerformanceCounter, addr c2
    56.             fild qword ptr [rez]
    57.             fild qword ptr [f]
    58.             fild qword ptr [c2]
    59.             fild qword ptr [c1]
    60.             fsub
    61.             fdiv
    62.             fmul
    63.             fistp qword ptr [rez]
    64.             ;вычислим результат с учетом теста data_input
    65.             mov eax, dword ptr [rez]
    66.             mov edx, dword ptr [rez+4]
    67.             sub eax, dword ptr [rez_data]
    68.             sbb edx, dword ptr [rez_data+4]
    69.             mov dword ptr [rez], eax
    70.             mov dword ptr [rez+4], edx
    71.             ;выведем на экран данные теста
    72.             invoke UtoA, rez, addr str_for_float
    73.             invoke SendMessage,hIDC_test_rez,WM_SETTEXT, NULL, addr str_for_float
    добавил конвертацию int64 в строку
    Код (Text):
    1. UtoA proc __num: qword, __buffer:dword
    2. .data
    3.   numtab db '0123456789ABCDEF'
    4. .code
    5.   ALIGN 4
    6.         mov     eax, dword ptr [__num]
    7.         mov     edx, dword ptr [__num+4]
    8.         mov     ecx, 10
    9.         mov     edi, [__buffer]
    10. ; edx:eax - number
    11. ; edi     - buffer
    12. ; ecx     - база счисления (от 2 до 16)
    13.         push    ebp
    14.         mov     ebp, esp        ; сохраняем верхушку стека
    15.         mov     esi, edx
    16.         mov     ebx, eax
    17.     __big:
    18.         cmp     esi, ecx
    19.         jb      __small
    20.         mov     eax, esi
    21.         xor     edx, edx
    22.         div     ecx             ; делим старшую часть
    23.         mov     esi, eax
    24.         mov     eax, ebx
    25.         div     ecx             ; теперь младшую, с учетом предыдущего деления
    26.         push    dword ptr [edx+numtab]
    27.         mov     edx, esi
    28.         jmp     __big
    29.     __small:
    30.         div     ecx
    31.         push    dword ptr [edx+numtab]
    32.         xor     edx, edx
    33.         or      eax, eax
    34.         jnz     __small
    35.     __store:
    36.         pop     eax
    37.         mov     [edi], al
    38.         inc     edi
    39.         cmp     ebp, esp
    40.         jne     __store
    41.         xor     eax, eax
    42.         pop     ebp
    43.         mov     [edi], al       ; завершающий нуль
    44.         ret
    45. UtoA endp
    результат - 1493901652715795 такого быть не может, если счетчик выдает в наносекундах....
    у меня опять косяк гдето :dntknw:
     
  4. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Во-первых, явная ошибка\опечатка:
    Во-вторых, или ты с физикой не дружишь или я чего не понимаю ;)
    Причем тут вообще rdtsc ? У тебя res_data = числу тактов процессора за ~100 мс, т.е. это частота проца Fcpu, деленная на 10. Затем ты ее умножаешь на f/(c2-c1), т.е. на величину обратную времени выполнения какой-то операции, т.е. опять на некую скорость. Спрашивается - что это за хитрая величина такая получается ?

    PS: надеюсь ты знаешь, что fdiv делит st1 на st0, т.е. f/(c2-c1)
     
  5. NetDemon

    NetDemon New Member

    Публикаций:
    0
    Регистрация:
    24 апр 2007
    Сообщения:
    22
    согласен... дурацкий пример передЁра того в чем слабо разбираешься...
    пошевелил мозгом, переделал на свое слабое разумение....
    задача:
    есть некая функция, лопатит данные в опр.адресе
    треб-ся:
    померить примерное время выполнения функции
    как делал:
    в цикле меряю скорость передачи данных в опр.адрес (с2-с1)/f
    далее в такомже цикле меряю скорость (передачи данных+функция) (с2-с1)/f
    вычитаю из второго рез-та первый, по идее должен получить примерное время выполнения функции
    Результат:
    1. часто время выполнения функции + передача данных почему-то меньше времени просто передачи данных :dntknw:
    2. часто что цикл10, что 10000 =рез-тат = e-4
    Код (Text):
    1.                        invoke SendMessage,hIDC_n_cicl,WM_GETTEXT, 15, addr str_for_float
    2.             invoke ustr2dw, addr str_for_float
    3.             mov Ncicl, eax
    4.             mov iCicl, eax
    5.             invoke  GetCurrentProcess
    6.             invoke  SetPriorityClass, eax, REALTIME_PRIORITY_CLASS
    7.             invoke  GetCurrentThread
    8.             invoke  SetThreadPriority, eax, THREAD_PRIORITY_TIME_CRITICAL
    9.             invoke  Sleep, 10
    10.             ;тест  data_input
    11.             invoke QueryPerformanceFrequency, addr f
    12.             invoke QueryPerformanceCounter, addr c1
    13.             invoke Sleep,100
    14.             ;----
    15.           ii: invoke data_input, pBuffNMPTH, addr hIDC_1_00, 16
    16.             dec [iCicl]
    17.             jnz ii
    18.             ;----
    19.             invoke QueryPerformanceCounter, addr c2
    20.             fild qword ptr f
    21.             fild qword ptr c2
    22.             fild qword ptr c1
    23.             fsub
    24.             fdiv st,st(1)
    25.             fxch st(1)
    26.             ;тест собственно процедуры
    27.             invoke QueryPerformanceCounter, addr c1
    28.             invoke Sleep,100
    29.             ;----
    30.           iii: invoke data_input, pBuffNMPTH, addr hIDC_1_00, 16
    31.             push pBuffNMPTH
    32.             call simpl_1
    33.             dec [Ncicl]
    34.             jnz iii
    35.             ;----
    36.             invoke QueryPerformanceCounter, addr c2
    37.             fild qword ptr c2
    38.             fild qword ptr c1
    39.             fsub
    40.             fdiv st,st(1)
    41.             fsub st,st(2)
    42.             fstp qword ptr rez
    43.             invoke FloatToStr, rez, addr str_for_float
    44.             invoke SendMessage,hIDC_test_rez,WM_SETTEXT, NULL, addr str_for_float
     
  6. asmfan

    asmfan New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2006
    Сообщения:
    1.004
    Адрес:
    Abaddon
    NetDemon
    Товагисч, пешите на другом языке - вот ответ. Не вижу повода писать на ассемблере, ну нету тут в вашем коде ничего критичного к скорости выполнения, а лопатить всё это смог один многоуважаемый leo.
    способ 2. Изучите дебаггер. ну хоть какой-нибудь, тем самым вы поможите себе и облегчите жизнь другим.
     
  7. NetDemon

    NetDemon New Member

    Публикаций:
    0
    Регистрация:
    24 апр 2007
    Сообщения:
    22
    asmfan
    не вижу повода писать ответ, если неудосужился даже прочитать....
    способ 2. из разряда, собрался копать землю - копай лопатой...
    а я по твоему руками копаю?! ;)
    А насчет лопатить... если б я не привел код, что ты написал бы?
    догадываюсь... "че тут тебе телепаты?" :)
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    NetDemon
    Самокритично пошевелил, но видимо не достаточно интенсивно ;)
    Sleep(100) зачем оставил ? Если для проверки насколько точно винда 0.1 сек выдерживает, то и без проверки известно, что хреново (+-15 мсек). Вот тебе и ответ на вопросы 1) и 2)

    Еще не понятно зачем ты дважды пытаешься измерять время некого data_Input - уверен, что результаты должны быть стабильными ? По крайней мере при первом вызове любой процедуры и первом обращении к данным с большой вероятностью получишь завышенные значения если соотв.кода и данных еще нет в кэше, не настроена схема предсказания переходов и т.п. Лучше замерять передачу и функцию отдельно или вместе, но без всяких вычитаний

    Ну и наконец нужно приучаться чистить за собой стек FPU - у тебя в итоге в FPU остаются два значения, а это чревато последствиями. Последние три инструкции можно переписать так
    Код (Text):
    1. fdivrp
    2. fsubrp
    3. fstp qword ptr rez
     
  9. NetDemon

    NetDemon New Member

    Публикаций:
    0
    Регистрация:
    24 апр 2007
    Сообщения:
    22
    leo
    1.Насчет слипа гдето читал, что нужно притормазить, чтобы не было лишних обращений изза многопоточности windows (немного сумбурно, но надеюсь понятно :) )
    2.Некий data_input собирает значения с edit control'ов, конвертит значения в float и помещает в опр. адрес.
    тк функция возвращает результат в тот же адрес, то цикл вызова функции после 1 прохода будет давать неправильный результат....
    а тк мне нужно примерную скорость самой функции, то зачем мне в нагрузку опрос памяти? который наверняка и сам нестабильный результат дает!!! :dntknw:
    А у меня ведь есть функции, у которых сбор данных может быть больше раза в два!
    3. Насчет FPU понял, учту...вопрос: А какие могут быть последствия?
     
  10. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Во-первых, про Sleep(100) ты врядли где-то читал, а просто скопипастил из примера определения частоты процессора, где ее использование несет совсем другой смысл ;)
    Во-вторых, есть рекомендация перед измерениями вызывать Sleep(0), при этом ты отдаешь винде неиспользованные остатки кванта времени потока и (с определенной долей вероятности) гарантируешь, что код после Sleep возобновиться в начале нового кванта и у тебя будет ~10-20 мс на проведение измерений. Но при использовании реал-тайм приоритета этот трюк мало что дает
    И в-третьих, самое главное - если хочешь, то используй любые Sleep, но зачем их во время измерения то включать ?!! Я же сказал, что Sleep имеет погрешность ~15 мс, нафига тогда точные измерения по QueryXXX или rdtsc использовать. Если нужен плюс-минус километр, используй GetTickCount. В общем, не дури и выкини эти sleep вообще или хотя бы вытащи их перед invoke QueryPerformanceCounter, addr c1

    Ужас, представляешь какие это тормоза и нестабильности !
    Во-первых, намного быстрее и стабильнее было бы один раз вызвать data_input, поместив данные в дополнительный буфер, а в цикле измерений копировать его pBuffNMPTH
    Во-вторых, если время выполнения самой функции simpl_1 не слишком маленькое, то можно измерять непосредственно его, переместив QueryXXX внутрь цикла (можно суммировать значения, а можно просто выводить серию из скажем 10-ти значений и смотреть их разброс). Если значения окажутся = 0, то заменить Query на rdtsc
    Код (Text):
    1.   xor eax,eax
    2.   mov dword ptr [c2],eax
    3.   mov dword ptr [c2+4],eax
    4. iii:
    5.   invoke data_input, pBuffNMPTH, addr hIDC_1_00, 16
    6.   ;---
    7.   rdtsc
    8.   mov dword ptr [c1],eax
    9.   mov dword ptr [c1+4],edx
    10.   ;---
    11.   push pBuffNMPTH
    12.   call simpl_1
    13.   ;---
    14.   rdtsc
    15.   sub eax,dword ptr [c1]
    16.   sbb edx,dword ptr [c1+4]
    17.   add dword ptr [c2],eax
    18.   adc dword ptr [c2+4],edx
    19.   ;---
    20.   dec [Ncicl]
    21.   jnz iii
    Последствия от неполной очистки стэка FPU могут быть плачевными - переполнение оного стэка, выброс исключения и как следствие - кранты твоей проге (т.к. SEH ты видимо не используешь). В данной конкретной проге тебе может и повезло, что FloatToStr использует мало регистров FPU, но в другой ситуации может и не повезти. И еще - раз функция data_input тоже работает с FPU, то оставлять в нем значения частоты и первого измерения тоже не хорошо. Негласное правило - при выходе из своей функции и перед вызовом других функций, которые могут работать с FPU, нужно обязательно очищать все его регистры