Несколько лет назад я описывал четыре способа измерения точного времени выполнения операции в Delphi. Сегодня рассмотрим этот же вопрос, но уже применительно к Lazarus и Free Pascal. Учитывая, что Lazarus можно использовать как в Windows, так и в Linux, способы того, как измерить точное время выполнения операции в Lazarus и Delphi будут немного различаться. Для начала, определимся с тем, время выполнения какой операции мы будем измерять. Путь это будет вот такая простенькая процедура:
procedure DoSomething; var i,k: QWord; begin k:=0; for i:=0 to 999999999 do inc(k); end;
Учитывая то, что на данный момент я могу проверить работоспособность того или иного способа только на двух платформах: Windows и Linux, ниже я буду давать описание применимости различных способов точного измерения времени, исходя из проверок именно на этих платформах.
Способ №1 — использование функции Now()
Платформы: Windows, Linux.
Как и в случае с Delphi этот способ вполне подойдет для измерения времени, например, следующим образом:
procedure TForm1.Button2Click(Sender: TObject); var start, stop: TDateTime; begin start:=Now;//засекаем начальное время DoSomething; stop:=Now;//засекаем конечное время {выводим время выполнения операции с точностью до секунды} Memo1.Lines.Add(SecondsBetween(start, stop).ToString); end;
И, хотя в справке Free Pascal ничего не сказано про точность функции Now() (в справке Delphi написано, что точность составляет порядка секунды), я не стал рисковать и все также использовал функцию SecondsBetween() из модуля DateUtils для расчёта количества секунд, пошедших на выполнение операции.
Способ №2 — использование функции GetTickCount
Платформы: Windows, Linux
В настоящее время, в модуле sysutils Free Pascal содержатся две функции, одна из которых помечена как deprecated:
function GetTickCount: LongWord; deprecated 'Use GetTickCount64 instead'; function GetTickCount64: QWord;
Как и в случае с Delphi, используя GetTickCount можно получить время в миллисекундах, затраченное на выполнение операции. Однако, в справке Free Pascal про эту функцию сказано следующее: функция полезна для измерения времени, но не следует делать никаких предположений относительно интервала между тиками. В принципе, примерно тоже сказано и в справке Windows относительно их функций GetTickCount.
Использование функций:
procedure TForm1.Button1Click(Sender: TObject); var Start, Stop: QWord; begin Start:=GetTickCount64; DoSomething; Stop:=GetTickCount64; Memo1.Lines.Add((stop-start).ToString); end;
Стоит отметить, что в зависимости от платформы на которой собирается приложение, GetTickCount64 имеет различные реализации. Так, например, если мы собираем приложение под Windows, то GetTickCount64 будет использовать одноименную функцию из модуля Windows:
function GetTickCount64: QWord; {$IFNDEF WINCE} var lib: THandle; {$ENDIF} begin {$IFNDEF WINCE} { on Vista and newer there is a GetTickCount64 implementation } if Win32MajorVersion >= 6 then begin if not Assigned(WinGetTickCount64) then begin lib := LoadLibrary('kernel32.dll'); WinGetTickCount64 := TGetTickCount64( GetProcAddress(lib, 'GetTickCount64')); end; Result := WinGetTickCount64(); end else {$ENDIF} Result := Windows.GetTickCount; end;
Если же мы используем Linux, то реализация GetTickCount64 основывается на использовании clock_gettime() — функции из языка Си:
function GetTickCount64: QWord; var tp: TTimeVal; {$IFDEF HAVECLOCKGETTIME} ts: TTimeSpec; {$ENDIF} begin {$IFDEF HAVECLOCKGETTIME} if clock_gettime(CLOCK_MONOTONIC, @ts)=0 then begin Result := (Int64(ts.tv_sec) * 1000) + (ts.tv_nsec div 1000000); exit; end; {$ENDIF} fpgettimeofday(@tp, nil); Result := (Int64(tp.tv_sec) * 1000) + (tp.tv_usec div 1000); end;
Способ №3 — использование функций QueryPerformanceCounter и QueryPerformanceFrequency
Платформы: Windows
Использовать функции можно, подключив в uses модуль windows. Соответственно, под Linux метод использовать невозможно, а посмотреть реализацию способа в Windows можно в предыдущей статье про точное измерение времени выполнения операций.
Способ №4 — когда разбираться с функциями лень. Используем компонент TEpikTimer
По аналогии с классом TStopwatch из Delphi, компонент TEpikTimer дает нам широкие возможности по определению времени, затраченного на выполнение какой-либо операции. В принципе, этим компонентом я обычно и пользуюсь.
Чтобы установить компонент, заходим в меню «Пакет — Установить/удалить пакеты»
В списке «Доступные для установки» ищем пакет под названием etpackage_dsgn
Жмем кнопку «Установить выбранное» под списком и, потом, «Сохранить и перезапустить IDE». В новом окне жмем «Продолжить», Lazarus пересоберется и на вкладке System появится новый компонент TEpikTimer:
У компонента есть множество полезных функций, позволяющих измерить точное время выполнения операции в Lazarus, я продемонстрирую только одну из них.
procedure TForm1.Button3Click(Sender: TObject); begin EpikTimer1.Clear; //сбрасываем таймер EpikTimer1.Start; //засекаем время DoSomething; EpikTimer1.Stop; //останавливаем таймер {выводим время выполнения операции в секундах} Memo1.Lines.Add(EpikTimer1.Elapsed.ToString); end;
Здесь я вывел время выполнения операции в секундах. Также EpikTimer умеет выводить время в различных форматах.
Помимо представленных выше способов, вы можете также воспользоваться и другими способами измерения точного времени выполнения операции в Lazarus и Free Pascal, например компонентом TJclCounter от JEDI, однако, в любом случае, эти компоненты будут использовать один из способов, рассмотренных выше, как это делает EpikTimer.