уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Точное время измерения выполнения операции в Delphi может пригодится во многих случаях, начиная от самого простого — показать пользователю время, затраченное на выполнение длительной операции (здесь, кстати, высокая точность нужна редко) и, заканчивая, ситуациями, когда в целях оптимизации программы нам необходимо выявить в программе наиболее «узкие» места в которых программа «застревает» на длительный промежуток времени. В основном, последняя ситуация характерна при разработке программ, использующих и обрабатывающих большие массивы данных, когда скорость выполнения операций выходит, если не на первое, то на одно из первых мест в требованиях к приложению.
Есть несколько способов узнать время выполнения операций в Delphi и все эти способы, в принципе, рассмотрены как в Сети, так и моем блоге. Поэтому представленная ниже статья — это лишь объединение всех возможных способов измерения времени в Delphi и опытный Delphi-программист здесь врядли встретит что-то новое для себя.

Способ №1 — самый простой. Используем функцию Now()

Самый простейший и наименее точный способ измерить время, затраченное на выполнение какой-либо операции в Delphi — воспользоваться функцией Now() из модуля System.SysUtils.

Исходный код может выглядеть, например, так:

uses System.DateUtils;
....
var Start, Stop: TDateTime;
    Elapsed: int64;
begin
  Start:=Now; //засекли начало выполнения операции
  DoSomething;//выполняем что-либо
  Stop:=Now;  //засекли окончание выполнения операции
  Elapsed:=SecondsBetween(Start, Stop);//время в секундах
end;

Вполне вероятно, что у вас может возникнуть резонный вопрос: почему я использовал в примере SecondsBetween(), а не, например, MilliSecondsBetween() для большей точности? Сделал я это, опираясь исключительно, на описание функции Now() в официальной справке по Delphi, которая гласит следующее: «Несмотря на то, что в TDateTime могут передаваться миллисекунды, Now() имеет точность до ближайшей секунды«. То есть, если использовать Now(), то определять интервал времени с точностью до миллисекунд — не имеет смысла.

Способ №2 — используем Windows API. Функция GetTickCount().

Функция GetTickCount() не имеет параметров и возвращает количество миллисекунд, прошедших с момента запуска системы. Судя по официальной справке Microsoft, разрешение функции GetTickCount() ограничено разрешением системного таймера, которое обычно находится в диапазоне от 10 до 16 миллисекунд. При этом, счётчик миллисекунд будет обнулен, если система запущена более 49,7 дней.
В принципе, пример использования этой функции похож на предыдущий:

var Start, Stop: cardinal;
  Elapsed: cardinal;
begin
  Start:=GetTickCount; //засекли начало выполнения операции
  DoSomething;//выполняем что-либо
  Stop:=GetTickCount; //засекли окончание выполнения операции
  Elapsed:=Stop-Start;//время в миллисекундах
end;

Так, используя функцию GetTickCount() мы можем засечь время выполнения операции в Delphi с точностью до миллисекунды. Если и такая точность Вам не подходит и необходимо измерить интервал времени ещё точнее, то следующий способ — для вас.

Способ №3 — продолжаем использовать Windows API. Функции QueryPerformanceCounter и QueryPerformanceFrequency

Об этих функциях я рассказывал давным-давно, но, повторюсь ещё раз.

QueryPerformanceCounter — извлекает текущее значение счетчика производительности, которое представляет собой метку времени с высоким разрешением (<1 мкс), которая может использоваться для измерения временных интервалов.
QueryPerformanceFrequency — извлекает частоту счетчика производительности. Частота счетчика производительности фиксируется при загрузке системы и согласована во всех процессорах,поэтому значение нужно запрашивать только при инициализации приложения, а результат может быть кэширован.

Для того, чтобы воспользоваться этими функциями для отсчёта интервала времени, затраченного на выполнение какой-либо операции в Delphi нам необходимо оформить исходный код, например, таким образом:

var iCounterPerSec: TLargeInteger;
    T1, T2: TLargeInteger; //значение счётчика ДО и ПОСЛЕ операции
begin
  QueryPerformanceFrequency(iCounterPerSec);//определили частоту счётчика 
  QueryPerformanceCounter(T1); //засекли время начала операции
 
  DoSomething; //выполнили что-то
 
  QueryPerformanceCounter(T2);//засекли время окончания
  ShowMessage(FormatFloat('0.0000', (T2 - T1)/iCounterPerSec) + ' сек.');//вывели количество секунд на выполнение операции
end;

С полученными значениями T1 и T2 можно «играться» как угодно, например, выводить отдельно минуты/секунды/миллисекунды и т.д. тут всё зависит от ваших потребностей и желаний, я же показал наиболее простой пример использования счётчика с высоким разрешением в Delphi.

Способ №4 — используем возможности Delphi. Модуль System.Diagnostics

Об этом модуле я также упоминал вскользь в некоторых статьях блога, например, здесь. Модуль этот появился в Delphi уже достаточно давно (могу ошибаться, но, по-моему с Delphi XE-XE2). В модуле представлена всего одна запись (record) — TStopwatch, которая является ни чем иным, как удобной «обёрткой» для использования таймеров высокого разрешения из примера выше. Судя по достаточно скромной справке, TStopwatch использует функциональные возможности, зависящие от операционной системы, для получения доступа к таймерам с высоким разрешением, если они доступны. Если таймеры с высоким разрешением в ОС недоступны, то используются обычные таймеры.

Несмотря на то, что TStopwatch — это запись, для корректного использования всё же необходимо вызывать метод Create или StartNew.

Описание TStopwatch следующее:

TStopwatch = record
  strict private
    class var FFrequency: Int64;
    class var FIsHighResolution: Boolean;
    class var TickFrequency: Double;
  strict private
    FElapsed: Int64;
    FRunning: Boolean;
    FStartTimeStamp: Int64;
    function GetElapsed: TTimeSpan;
    function GetElapsedDateTimeTicks: Int64;
    function GetElapsedMilliseconds: Int64;
    function GetElapsedTicks: Int64;
    class procedure InitStopwatchType; static;
  public
    class function Create: TStopwatch; static;
    class function GetTimeStamp: Int64; static;
    procedure Reset;
    procedure Start;
    class function StartNew: TStopwatch; static;
    procedure Stop;
    property Elapsed: TTimeSpan read GetElapsed;
    property ElapsedMilliseconds: Int64 read GetElapsedMilliseconds;
    property ElapsedTicks: Int64 read GetElapsedTicks;
    class property Frequency: Int64 read FFrequency;
    class property IsHighResolution: Boolean read FIsHighResolution;
    property IsRunning: Boolean read FRunning;
  end;
  • Свойство IsHighResolution указывает, основан ли таймер на счетчике производительности с высоким разрешением.
  • Метод Start() начинает измерять прошедшее время.
  • Метод Stop() останавливает измерение прошедшего времени.
  • Свойство ElapsedMilliseconds получает общее истекшее время в миллисекундах.
  • Свойство Elapsed получает истекшее время в виде TTimeSpan.

Воспользоваться возможностями TStopwatch также достаточно просто, например, так:

uses System.Diagnostics;
...
var SW: TStopwatch;
begin
  SW:=TStopwatch.StartNew;
  SW.Start;
 
  DoSomething;
 
  SW.Stop;
  ShowMessage(SW.Elapsed.TotalMinutes.ToString);
end;

В представленном выше примере мы воспользовались TStopwatch и вывели количество минут (с дробной частью), пошедших на выполнение какой-то операции. В целом же, используя возможность TStopwatch.Elapsed можно выводить любые значения, ограниченные лишь возможностями TTimeSpan (см. справку).

Подведем итог

Итак, чтобы измерить точное время выполнения операции в Delphi, необходимо, прежде всего определиться с тем какая точность Вас устроит? Если достаточно, чтобы интервал времени определялся с точностью до секунды, то достаточно воспользоваться обычной, давно известной функцией Now(). Да, точность самая низкая, но, зато — просто.

Если ваше требование к точности измерения времени ограничивается миллисекундой — используйте GetTickCount(): просто, достаточно надежно (только, если вы не планируете измерять интервал времени больше 49,7 дней).

Если же требуется использовать самые точные методы измерения времени на выполнение операции, то тут либо связка функций QueryPerformanceCounter() и QueryPerformanceFrequency(), либо более удобный способ — использовать TStopwatch из модуля System.Diagnostics.

Книжная полка

Автор:Антон Григорьев
Название: О чем не пишут в книгах по Delphi
Описание Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API.
Купить на ЛитРес
Автор: Владимир Шапоров, Дмитрий Кузан
Название: Программирование Win32 API в Delphi
Описание: Рассмотрено применение различных интерфейсов прикладного программирования Windows (Win32 API) при разработке приложений с использованием Delphi. Описаны основы работы с API
Купить на ЛитРес

 

4 3 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
4 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Alex
Alex
06/02/2018 16:26

Спасибо, не знал о TStopwatch хоть и использую ХЕ3 и ХЕ8

Анонимно
Анонимно
08/12/2018 00:09

Спасибо за информацию, брат…

Lukas
Lukas
12/02/2019 20:25

Спасибо бро, ты лучший! Все возможные варианты в одном месте в структурированном виде! Это афигенная статья — не осталось ни одного вопроса. Только не бросай сайт, это походу единственный адекватный сайт по Delphi.