Расчет скорости рутин?

34

Каким будет лучший и наиболее точный способ определить, сколько времени потребовалось для обработки процедуры, например, процедуры функции?

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

Я рассматривал использование GetTickCount, но я не уверен, что это будет что-то близкое к точности?

Было бы полезно иметь восстановимую функцию/процедуру для вычисления времени подпрограммы и использовать ее примерно так:

// < prepare for calcuation of code
...
ExecuteSomeCode; // < code to test
...
// < stop calcuating code and return time it took to process

Я с нетерпением жду некоторых предложений.

Спасибо.

Craig.

Теги:
optimization
performance
gettickcount

8 ответов

22
Лучший ответ

Насколько мне известно, наиболее точный метод - это QueryPerformanceFrequency:

код:

var
  Freq, StartCount, StopCount: Int64;
  TimingSeconds: real;
begin
  QueryPerformanceFrequency(Freq);
  QueryPerformanceCounter(StartCount);
  // Execute process that you want to time: ...
  QueryPerformanceCounter(StopCount);
  TimingSeconds := (StopCount - StartCount) / Freq;
  // Display timing: ... 
end; 
  • 0
    Я пытался так: 'процедура TForm1.Button1Click (Отправитель: TObject); var i: Integer; begin QueryPerformanceFrequency (Freq); QueryPerformanceCounter (StartCount); для i: = от 0 до 10000 начинайте ListBox1.Items.Add ('Item' + IntToStr (i)); конец; QueryPerformanceCounter (StopCount); TimingSeconds: = (StopCount - StartCount) / Freq; ShowMessage (IntToStr (Round ((TimingSeconds)))); конец;' Он возвращается как в секундах, как вернуть это в миллисекундах?
  • 0
    здесь у вас есть полностью функциональный пример - delphi.about.com/od/windowsshellapi/a/…
Показать ещё 12 комментариев
18

Попробуйте Eric Grange Пробоотборщик пробоотбора.

  • 0
    +1. Это хороший профилировщик.
  • 0
    +1 очень хороший и эффективный профайлер.
Показать ещё 6 комментариев
13

От Delphi 6 вверх вы можете использовать счетчик timestamp x86.
Это рассчитывает циклы процессора, на 1 ГГц процессоре, каждый счет занимает одну наносекунду.
Не может быть более точным.

function RDTSC: Int64; assembler;
asm
  // RDTSC can be executed out of order, so the pipeline needs to be flushed
  // to prevent RDTSC from executing before your code is finished.  
  // Flush the pipeline
  XOR eax, eax
  PUSH EBX
  CPUID
  POP EBX
  RDTSC  //Get the CPU time stamp counter.
end;

В x64 следующий код более точен, потому что он не страдает от задержки CPUID.

  rdtscp        // On x64 we can use the serializing version of RDTSC
  push rbx      // Serialize the code after, to avoid OoO sneaking in
  push rax      // subsequent instructions prior to executing RDTSCP.
  push rdx      // See: http://www.intel.de/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf
  xor eax,eax
  cpuid
  pop rdx
  pop rax
  pop rbx
  shl rdx,32
  or rax,rdx

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

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

Почему вы не должны использовать QueryPerformanceCounter()
QueryPerformanceCounter() дает такое же количество время, если процессор замедляется, он компенсирует потерю CPU. Пока RDTSC даст вам такое же количество циклов, если ваш процессор замедляется из-за перегрева или еще чего-то еще.
Поэтому, если ваш процессор начинает работать горячим и ему нужно дросселировать, QueryPerformanceCounter() скажет, что ваша процедура занимает больше времени (что вводит в заблуждение), и RDTSC скажет, что он принимает такое же количество циклов (что точно).
Это то, чего вы хотите, потому что вас интересует количество циклов процессора, используемых вашим кодом, а не время настенных часов.

Из последних документов Intel: http://software.intel.com/en-us/articles/measure-code-sections-using-the-enhanced-timer/?wapkw=%28rdtsc%29

Использование часов процессора

Этот таймер очень точен. В системе с процессором 3GHz этот таймер может измерять события, длившиеся менее одной наносекунды. [...] Если частота изменяется во время работы целевого кода, окончательное считывание будет избыточным, поскольку начальное и окончательное считывание не было выполнено с использованием той же тактовой частоты. Количество тактов синхронизации, которые произошли за это время, будет точным, но прошедшее время будет неизвестно.

Если не использовать RDTSC
RDTSC полезен для базового времени. Если вы используете многопоточный код на одном процессоре, RDTSC будет работать нормально. Если у вас несколько процессоров, стартовый номер может исходить от одного процессора и конечного номера от другого.
Поэтому не используйте RDTSC для многопоточного кода на многопроцессорной машине. На одном процессоре процессора он работает отлично, или однопоточный код на многопроцессорной машине тоже хорош.
Также помните, что RDTSC подсчитывает циклы процессора. Если есть что-то, что требует времени, но не использует CPU, например disk-IO или network, чем RDTSC, это не очень хороший инструмент.

Но в документации говорится, что RDTSC не точен на современных процессорах
RDTSC не инструмент для отслеживания времени, это инструмент для отслеживания циклов CPU.
Для этого он является инструментом только. Регуляции, которые отслеживают время, не точны на современном процессоре, потому что часы процессора не такие абсолютные, как раньше.

  • 3
    RDTSC не должен использоваться в Windows с современным процессором. Фактически, его результаты зависят от текущего состояния процессора: современные процессоры могут изменять его частоту (например, технология TurboBoost), а многоядерный дизайн затрудняет получение точных результатов, поэтому rdtsc НЕ ДОЛЖЕН использоваться. QueryPerformanceCounter () IS должен использоваться вместо этого. См. Msdn.microsoft.com/en-us/library/ee417693%28VS.85%29.aspx
  • 2
    @Bouchez, это именно то, почему вы хотите использовать RDTSC! Если ваш процессор замедляется, RDTSC по-прежнему будет показывать правильное время работы в циклах. Что касается проблем с многопоточностью, стандартный код Delphi является однопоточным, поэтому проблема возникает только при использовании нескольких потоков. Это QueryPerformanceCounter() , который не дает точных результатов, когда процессор замедляется.
Показать ещё 8 комментариев
10

Вы не указали свою версию Delphi, но Delphi XE имеет TStopWatch, объявленную в модуле Diagnostics. Это позволит вам измерить время выполнения с разумной точностью.

uses
  Diagnostics;
var
  sw: TStopWatch;
begin
  sw := TStopWatch.StartNew;
  <dosomething>
  Writeln(Format('runtime: %d ms', [sw.ElapsedMilliseconds]));
end;
  • 1
    У меня нет Delphi XE, но я считаю, что это тот же класс, что и здесь delphi.about.com/od/windowsshellapi/a/…
  • 0
    В XE я бы написал в AQ Time, чтобы точно понять, где важна производительность.
Показать ещё 3 комментария
6

Я спрашиваю, потому что в настоящее время я пытаюсь оптимизировать несколько функций

Естественно думать, что измерение - это то, как вы узнаете, что оптимизировать, но там лучший способ.

Если что-то занимает достаточно большую часть времени (F), чтобы стоить оптимизировать, тогда, если вы просто производите паузу, F - вероятность того, что вы поймаете его в действии. Сделайте это несколько раз, и вы точно поймете, почему он это делает, вплоть до точных строк кода.

Подробнее об этом. Вот пример.

Исправьте его, а затем выполните общее измерение, чтобы увидеть, сколько вы сохранили, что должно быть около F. Промыть и повторить.

  • 0
    +1 за блестящее понимание. Но будьте осторожны, чтобы не оптимизировать цикл простоя :-).
  • 0
    @Johan: Спасибо за хедз-ап. Я буду осторожен :-)
1

Вот несколько процедур, которые я сделал для проверки продолжительности функции. Я застрял их в блоке, который я назвал uTesting, а затем просто бросаю в предложение uses во время моего тестирования.

Декларация

  Procedure TST_StartTiming(Index : Integer = 1);
    //Starts the timer by storing now in Time
    //Index is the index of the timer to use. 100 are available

  Procedure TST_StopTiming(Index : Integer = 1;Display : Boolean = True; DisplaySM : Boolean = False);
    //Stops the timer and stores the difference between time and now into time
    //Displays the result if Display is true
    //Index is the index of the timer to use. 100 are available

  Procedure TST_ShowTime(Index : Integer = 1;Detail : Boolean = True; DisplaySM : Boolean = False);
    //In a ShowMessage displays time
    //Uses DateTimeToStr if Detail is false else it breaks it down (H,M,S,MS)
    //Index is the index of the timer to use. 100 are available

объявленные переменные

var
  Time : array[1..100] of TDateTime;

Реализация

  Procedure TST_StartTiming(Index : Integer = 1);
  begin
    Time[Index] := Now;
  end; 

  Procedure TST_StopTiming(Index : Integer = 1;Display : Boolean = True; DisplaySM : Boolean = False);
  begin
    Time[Index] := Now - Time[Index];
    if Display then TST_ShowTime;
  end;

  Procedure TST_ShowTime(Index : Integer = 1;Detail : Boolean = True; DisplaySM : Boolean = False);
  var
    H,M,S,MS : Word;
  begin
    if Detail then
      begin
        DecodeTime(Time[Index],H,M,S,MS);
        if DisplaySM then
        ShowMessage('Hour   =   ' + FloatToStr(H)  + #13#10 +
                    'Min     =   ' + FloatToStr(M)  + #13#10 +
                    'Sec      =   ' + FloatToStr(S)  + #13#10 +
                    'MS      =   ' + FloatToStr(MS) + #13#10)
        else
        OutputDebugString(PChar('Hour   =   ' + FloatToStr(H)  + #13#10 +
                    'Min     =   ' + FloatToStr(M)  + #13#10 +
                    'Sec      =   ' + FloatToStr(S)  + #13#10 +
                    'MS      =   ' + FloatToStr(MS) + #13#10));
      end
    else
      ShowMessage(TimeToStr(Time[Index]));
      OutputDebugString(Pchar(TimeToStr(Time[Index])));
  end;
0

clock_gettime() - это высокое решение, точное с точностью до nano секунд, вы также можете использовать rtdsc, что является точным для цикла процессора, и, наконец, вы можете просто использовать gettimeofday().

0

Ещё вопросы

Сообщество Overcoder
Наверх
Меню