Расчет скорости рутин? - PullRequest
34 голосов
/ 17 мая 2011

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

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

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

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

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

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

Спасибо.

Craig.

Ответы [ 8 ]

23 голосов
/ 17 мая 2011

Насколько мне известно, наиболее точным методом является использование 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; 
18 голосов
/ 17 мая 2011
13 голосов
/ 17 мая 2011

Начиная с Delphi 6 и выше, вы можете использовать счетчик меток времени 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's 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() дает ту же сумму время , если процессор замедляется, это компенсирует его удушение.В то время как RDTSC даст вам такое же количество циклов, если ваш ЦП замедляется из-за перегрева или чего-то еще.
Так что, если ваш ЦП начинает сильно нагреваться и ему нужно снизить скорость, QueryPerformanceCounter() скажет, что ваша процедура занимает больше времени (что вводит в заблуждение) и RDTSC скажет, что требуется одинаковое количество циклов (что точно) .
Это то, что вы хотите, потому что вас интересует суммациклов ЦП, которые использует ваш код, а не время настенных часов.

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

Использование тактовых сигналов процессора

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

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

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

10 голосов
/ 17 мая 2011

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

uses
  Diagnostics;
var
  sw: TStopWatch;
begin
  sw := TStopWatch.StartNew;
  <dosomething>
  Writeln(Format('runtime: %d ms', [sw.ElapsedMilliseconds]));
end;
6 голосов
/ 17 мая 2011

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

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

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

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

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

1 голос
/ 17 мая 2011

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

Декларация

  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 голосов
/ 30 ноября 2016

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

0 голосов
/ 03 мая 2012
...