| Как замерить время выполнения программы? | |
| Стандартный Паскаль | Free Pascal Compiler |
| Использование процедуры GetTime | <-- Этот метод тоже доступен |
| Работа с ячейкой памяти $0000:$046C | |
| Использование счетчика тактов процессора (RDTSC) | <-- Немного по-другому, но смысл тот же |
| Использование GetTickCount() | |
| Использование QueryPerformanceCounter() | |
Внимание !!!
При работе под Windows задача определения точного времени выполнения программы в общем случае не имеет решения... Дело в том, что в
Windows существует планировщик заданий, который может в любой момент переключиться с Вашего приложения на другое. MSDN советует
не делать никаких предположений о моменте переключения задач, следовательно, всегда существует вероятность того, что в
определенный момент времени после запоминания начального этапа работы программы, но перед запоминанием конечного
этапа произойдет вытеснение Вашей задачи планировщиком, и результат засекания времени окажется непредсказуемым...
Будем надеяться, что этого не произойдет...
Использование процедуры GetTime
Function GetTime: LongInt;
Var h, m, s, ms: Word;
begin
Dos.GetTime(h, m, s, ms);
GetTime := ms + 100 * (s + 60 * (m + 60 * h));
end;
...
{ Вызывать вот так: }
start := GetTime;
... { Здесь - алгоритм, время выполнения которого надо замерить }
WriteLn('Время выполнения = ', GetTime - start);
...
Естественно, срабатывает так называемое "золотое правило" - поскольку способ самый простой, у него много недостатков, в частности,
результат будет неверным, если во время работы алгоритма (после первого вызова функции GetTime, но перед вторым) сменится дата... Тогда
данный способ может показать и отрицательное время, но в любом случае оно будет неправильным (даже если будет правдоподобным)...
Работа с ячейкой памяти $0000:$046C
Var time: LongInt;
...
time := MemL[0:$046C];
... { Здесь - собственно программа }
time := MemL[0:$046C] - Time;
If time < 0 Then time := time + $1800B0;
WriteLn('Время выполнения = ', (time / 18.2):6:2);
...
Использование счетчика тактов процессора (RDTSC)
.MODEL LARGE .CODE .386c PUBLIC READTSC READTSC PROC FAR enter 0, 0 push ds db 0fh, 31h; RDTSC opcode mov bx, ss:[bp+8] mov ds, bx mov bx, ss:[bp+6] mov [bx], eax mov [bx+4], edx pop ds leave retf 4 READTSC ENDP ENDsample.pas
{$N+}
procedure ReadTSC(Var counter : Comp); far; external;
{$L tsc.obj}
Var a1, a2, a3 : Comp;
begin
ReadTSC(a1);
Writeln('Working :)'); { <--- Засекаем время выполнения этой строки }
ReadTSC(a2);
Writeln(a1:20:0, ', ', a2:20:0, ', ', (a2-a1):20:0)
end.
Примечание:function rdtsc:Int64;
asm
rdtsc
mov dword ptr [Result], eax
mov dword ptr [Result + 4], edx
end;
var
Start, Finish, Duration:Int64;
begin
Start := rdtsc;
writeln('test'); // Посмотрим, за сколько тактов выполняется WriteLn
Finish := rdtsc;
Duration := (Finish - Start);
WriteLn('Время выполнения (в тактах) = ', Duration);
end.
Использование GetTickCount() (32-битные компиляторы)
uses Windows;
var Duration: Cardinal;
begin
Duration := GetTickCount();
... // замеряемый блок
Duration := GetTickCount() - Duration;
WriteLn('Время выполнения = ', Duration);
end.
Этот метод также не отличается особой точностью (предельно малые интервалы времени при использовании GetTickCount() составляют 10 .. 16 мс),
но зато он очень прост, и если очень высокая точность не нужна - очень даже имеет право на существование.
Использование QueryPerformanceCounter()
(32-битные компиляторы)
Uses
Windows, SysUtils;
Var
t1, t2, Res: int64;
bOk: BOOL;
Procedure StartTimer;
Begin
t1 := 0; t2 := 0; Res := 0;
bOK := QueryPerformanceFrequency(Res);
If bOK Then QueryPerformanceCounter(t1);
end;
Procedure StopTimer;
Begin
If bOK Then QueryPerformanceCounter(t2);
End;
{ Время выполнения этой процедуры будет засекаться }
Procedure SomeProc;
Var
i: Integer;
a, b, T: Integer;
Begin
For i := 1 To 10000 Do Begin
a := 100; b := 250;
T := a; a := b; b := T;
End;
End;
Procedure CheckExecutionTime;
Begin
StartTimer;
SomeProc; // Запускаем тестируемую процедуру
StopTimer;
If bOK Then Writeln('Execution time: ' + Format('%g sec.',[(t2 - t1) / Res]));
End;
{ Изменяем приоритет потока на TIME_CRITICAL для увеличения точности }
Procedure Check_Time;
Var tp, pc: DWORD;
Begin
tp := GetThreadPriority(GetCurrentThread());
pc := GetPriorityClass(GetCurrentProcess());
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
Try
CheckExecutionTime()
Finally
SetThreadPriority(GetCurrentThread(), tp);
SetPriorityClass(GetCurrentProcess(), pc);
End
End;
{ Основная программа }
begin
check_time();
end.
(С) Krid, forum.sources.ru