1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
rogram Tower_of_hanoi; uses CRT; procedure Hanoi (n : integer; x, y, z : char); begin if n <> 0 then begin Hanoi (n - 1, x, z, y); writeLn ('Перемещаем диск ', n, ' c ', x, ' на ', y); Hanoi (n - 1, z, y, x) end end; var disk : integer; begin ClrScr; write ('Сколько дисков ?'); readLn (disk); GetTime(hour,min,sec,hund);//определяем время начала программы writeln(hour,':',min,':',sec,':',hund); t1:=sec*100+min*6000+hund; Hanoi (disk, 'X', 'Y', 'Z') GetTime(hour,min,sec,hund);//время окончания writeln(hour,':',min,':',sec,':',hund); t2:=sec*100+min*6000+hund;; t:=t2-t1;//прошло времени end. |
Время выполнения программы (Паскаль)
Часто возникает необходимость узнать время выполнения программы, написанной на языке Turbo Pascal. Для этих целей можно воспользоваться приведенным ниже кодом. Вам надо только вставить код вашей программы вместо комментария. Говорить о том, что вы вычислите точное время работы вашей программы нельзя (почему). Но для сравнения времени работы разных кодов — вполне будет достаточно. Можете высказать свое мнение о программке.
2010-11-11 • Просмотров [ 7880 ]
Порядок вывода комментариев: uses crt; var n,i:integer; VAR |
Как замерять время выполнения кода?
, Например сколько времени выполняется алгоритм
- Подписаться на тему
- Сообщить другу
- Скачать/распечатать тему
|
|
Самым простым способом является одновременно самый очевидный: В паскале есть стандартная процедура модуля Dos — GetTime. Её параметрамы должны быть обязательно переменные. После её выполнения в этих переменных будет содержаться время того момента, когда выполнится эта процедура:
Используя эту процедуру мы можем занести в переменные время до выполнения нужного нам действия, и в другие переменные время после выполнения нужного нам действия. Чтобы посчитать разницу нужно перевести время до выполнения и время после выполнения к количеству сотых частей секунды. Нельзя напрямую сравнивать к примеру просто секунды. Почему?
uses dos; var h1,m1,s1,t1:word; h2,m2,s2,t2:word; d:longint; begin gettime(h1,m1,s1,t1); … {любое действие, продолжительность которого измеряем} gettime(h2,m2,s2,t2); {вычислим время выполнения d — результат будет в сотых долях секунды} d:=(longint(h2)*360000+longint(m2)*6000+s2*100+t2)- {количество сотых долей секунды после выполнения} (longint(h1)*360000+longint(m1)*6000+s1*100+t1); {их количество до выполнения действия} writeln(‘Действие выполнялось ‘,d/100:0:2,’ секунды’); end. П.С. Следует добавить, что хотя этот способ и является самым простым, он отнюдь не самый точный, поскольку во время замера входит так же время выполнения процедуры GetTime, и кроме того это время может зависеть от состояния Директив компилятора. P.P.S:
function fGetTime: LongInt; var hr, min, sec, sec_100: word; begin GetTime(hr, min, sec, sec_100); fGetTime := longint(hr)*360000 + longint(min)*6000 + sec*100 + sec_100; end; { и работать с ней: } var before, after: longint; begin before := fGetTime; … after := fGetTime; writeln(‘Действие выполнялось ‘, (after — before) / 100:0:2,’ секунды’) end; V877 Сообщение отредактировано: volvo877 — 01.09.08, 07:35 |
Visitor |
|
Совсем забыл, что хотел запостить Для процессоров выше i486 существует способ очень точно сравнить время выполнения двух участков кода — ето встроенный в процессор счетчик тактов. Ниже приведены два исходника — процедура, считывающая значение етого счетчика, и пример ее использования на BP7.
.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 END sample.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. В прикрепленном файле то же самое, tsc.asm оттранслирован. P.S. Обратите внимание и сделайте выводы, как много времени занимает вывод на екран. |
Vesper |
|
Если не хочется подключать модуль DOS, замерить время можно с помощью взятия значения напрямую из памяти по адресу $0046C (4 байта). Там хранится число «тиков» встроенных часов реального времени, которые тикают 18.54 раз в секунду. И именно оттуда тащат его функции модуля.
procedure bogusprocedure;near; assembler; asm end; var int08save:pointer; int08handler_count:longint; int08handler_lessercount:word; int08handler_tickin:boolean; procedure int08handler;assembler;{no sense of far, it’s int handler, so must have iret inside} asm sti push ax push dx push ds mov ax,seg @data mov ds,ax {loading DS, probably it’s changed} mov al,int08handler_tickin or al,al je @hnd_l1 {if not tickin jump forward} db 66h inc word ptr int08handler_count @hnd_l1: dec int08handler_lessercount jne @hnd_l3 mov int08handler_lessercount,0100h {256, must be 65536 div (speedup level)} {ok, let’s call normal handler after all that} pushf call dword ptr int08save jmp @hnd_l2 @hnd_l3: mov al,20h out 20h,al {funny, BIOS caller uses call blablabla and ret instead of jmp short} db 0ebh,0 call bogusprocedure @hnd_l2: pop ds pop dx pop ax iret end;
Обработчик прерывания устанавливается на прерывание 08, перед этим нужно сохранить старый вектор вызовом getintvec($08,addr(int08save)), иначе процедура не будет работать
{programming timer channel 0, tickin 256, mode 3, hex counter, greater byte only} asm cli mov al,00100110b out 43h,al call bogusprocedure mov al,1 out 40h,al call bogusprocedure sti end; Вызов bogusprocedure стоит из-за того, что порт может не успеть принять записанный байт до того, как в него запишут следующий.
asm cli mov al,00110110b out 43h,al call bogusprocedure mov al,0 out 40h,al call bogusprocedure {ok, delay also goes here} out 40h,al call bogusprocedure sti end;
Естественно, нужно еще и вернуть старый вектор, setintvec($08,addr(int08save)); |
Vesper |
|
Не стал править предыдущий пост, он не расконвертился
time:=meml[0:$046C]; proces; time:=meml[0:$046C]-time; if time<0 then time:=time+$1800B0; writeln(time/18.2:6:2); Использован кусок кода Кришкина, немного подправленный. proces — процедура, решающая задачу. PS. Если в запрошенном куске будет запрос данных от пользователя, чистого времени вы уже не получите. Если, конечно, вам не нужно время реакции (или скорость набора) пользователя. Например, вам нужно узнать, сколько времени ваш ребенок (если есть, иначе ученик ) решал задачку в уме. Делаем так:
write(‘Задача №3: траляляляляляля. Введите ответ:’); time:=meml[0:$046C]; readln(x); time:=meml[0:$046C]-time; if time<0 then time:=time+$1800B0; writeln(‘Долговато пожалуй, целых ‘,time/18.2:6:2,’ секунд’); if x<>n then writeln(‘Кстати, ответ неверен.’) else writeln(‘Правильно.’); Хотя вряд ли вы будете ребенка пытать арифметикой в полночь. |
Romtek |
|
Moderator Рейтинг (т): 188 |
{ Это только интерфейсная часть кода. Целый код модуля находится в архиве } Unit XTIMER; INTERFACE Var elapsed: Longint; { прошедшее время, в милисекундах. } Procedure ClockOn; { включает счётчик времени } Procedure ClockOff; { выключает его } Procedure PrintTime; { выводит прошедшее время } IMPLEMENTATION END.
тестировалось также на Пентиумах II и III, без использования модуля CRT. Сообщение отредактировано: Romtek — 02.12.04, 12:22
|
Александр. |
|
Junior Рейтинг (т): 0 |
А на TMT можно использовать модуль ZenTimer. |
0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
0 пользователей:
- Предыдущая тема
- Pascal: Общие вопросы
- Следующая тема
[ Script execution time: 0,0340 ] [ 16 queries used ] [ Generated: 23.03.23, 06:02 GMT ]
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Несколько лет назад я описывал четыре способа измерения точного времени выполнения операции в 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.
4
2
голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
На олимпиадах все программы проверяются на специально подобранных тестовых заданиях, которые выполняются не более отведенного на это время исполнения программы на каждом тесте. Как в «домашних» условиях определить — сколько времени отработала ваша программа.
В TurboPascal была такая возможность — обратиться напрямую к памяти BIOS по адресу $0040:$006С (или $0000:$406C) и получить текущее время:
var t:longint absolute $40:$6c;
v:longint;
…{глобальные переменные}
begin
v:=t;{запомним начало времени выполнения программы}
…{основная часть программы}
writeln(‘Время выполнения программы- ‘,t-v);
end.
Как же оно в памяти распознается? Само время представляет из себя 20 бит, в котором 5 бит отводится на часы, по 6 бит на минуты и секунды, и 4 бита на миллисекунды. Например, вы получили при выводе v число 1058411, в двоичной системе это будет число:
00000 00000 00010000 00100110 01111011
Получим 16 часов 9 минут 38 секунд 11*4 миллисекунды (на миллисекунды отводится всего 15 значений, т.е. 60/15=4 каждое значение умножим на 4).
Но вот во FreePascal такая программа выдает ошибку в первой строке. Переписав ее с использованием функции Ptr(адрес), ни к чему не привело. В интернете нашла лишь сообщение, что эта функция работает не правильно.
var t,v:longint;
begin
t:=longint(ptr($40,$6c)^);
v:=t;
writeln(v); {выдает ноль}
…{основная часть программы}
t:=longint(ptr($40,$6c)^);
writeln(‘Время выполнения программы- ‘,t-v);
end.
Поэтому вопрос остался открытым. Но можно воспользоваться функциями модуля DOS, но для отправки на проверку этот модуль нужно будет удалить, так как по правилам олимпиад по программированию использование сторонних библиотек запрещено, в том числе и модуля CRT.
uses dos;
var p:integer;
h,m,s,g:word;
…{глобальные переменные}
begin
GetTime(h,m,s,g);
writeln(h,’:’,m,’:’,s,’:’,g);
p:=g+60*(s+60*(m+60*h));
… {основная часть программы}
GetTime(h,m,s,g);
p:=(g+60*(s+60*(m+60*h)))-p;
writeln(h,’:’,m,’:’,s,’:’,g);
writeln(‘Время выполнения программы- ‘,p);
end.
Для точного измерения времени в ОС Windows используется функция QueryPerformanceCounter. Ниже приведён пример (для Free Pascal) использования этой функции.
program Time;
uses
Windows;
var
start, finish, res: int64;
begin
QueryPerformanceFrequency(res); {Вызывается один раз в начале программы}
QueryPerformanceCounter(start);
{Участок кода, у которого будет замеряться время выполнения}
QueryPerformanceCounter(finish);
writeln('Затрачено ', (finish - start) / res, ' секунд' );
end.
В PascalABC.Net наиболее простым способом измерения времени являются использование функции Milliseconds, которая возвращает количество миллисекунд прошедших с момента запуска программы. Пример использования ниже.
program Time;
var
start, finish: integer;
i: longint;
x: double;
begin
start := Milliseconds;
// Start of computation (example)
for i := 1 to 1000000 do
x := sin(x + i);
// End of computation
finish := Milliseconds;
writeln('Execution time (ms): ', finish - start);
end.
rogram Tower_of_hanoi; uses CRT; procedure Hanoi (n : integer; x, y, z : char); begin if n <> 0 then begin Hanoi (n - 1, x, z, y); writeLn ('Перемещаем диск ', n, ' c ', x, ' на ', y); Hanoi (n - 1, z, y, x) end end; var disk : integer; begin ClrScr; write ('Сколько дисков ?'); readLn (disk); GetTime(hour,min,sec,hund);//определяем время начала программы writeln(hour,':',min,':',sec,':',hund); t1:=sec*100+min*6000+hund; Hanoi (disk, 'X', 'Y', 'Z') GetTime(hour,min,sec,hund);//время окончания writeln(hour,':',min,':',sec,':',hund); t2:=sec*100+min*6000+hund;; t:=t2-t1;//прошло времени end.