Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.15.150.178] |
|
Сообщ.
#1
,
|
|
|
Самым простым способом является одновременно самый очевидный:
Узнаём и запоминаем время, потом делаем нужное нам действие, и сразу после него опять узнаём и запоминаем время. Разница между временем до действия и временем после действия и будет временем выполнения действия. В паскале есть стандартная процедура модуля Dos - GetTime. Она узнаёт, сколько времени в момент её выполнения. Вот её синтаксис: procedure GetTime(var Hour, Minute, Second, Second100:word); Её параметрамы должны быть обязательно переменные. После её выполнения в этих переменных будет содержаться время того момента, когда выполнится эта процедура: Используя эту процедуру мы можем занести в переменные время до выполнения нужного нам действия, и в другие переменные время после выполнения нужного нам действия. После этого можно посчитать разницу во времени, и эта разница будет длительностью выполнения замеряемого действия. Чтобы посчитать разницу нужно перевести время до выполнения и время после выполнения к количеству сотых частей секунды. Нельзя напрямую сравнивать к примеру просто секунды. Почему? Предположим в момент замера времени до выполнения нужного нам действия было время 10 минут 58 секунд. А в момент замера времени после выполнения было 11 минут 15 секунд. Если мы вычтем из секунд-после секунды-до, мы получим (15-58)=-43 секунды. Но ведь это неверно %) Так получилось из-за того, что за время, пока выполнялась программа началась новая минута. Если же преобразовать всё время к количеству сотых частей секунды, то такой проблемы не возникнет. Вот пример действующего кода: 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: Можно пойти чуть дальше, и написать функцию fGetTime: 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 |
Сообщ.
#2
,
|
|
|
Совсем забыл, что хотел запостить
Для процессоров выше i486 существует способ очень точно сравнить время выполнения двух участков кода -- ето встроенный в процессор счетчик тактов. Ниже приведены два исходника -- процедура, считывающая значение етого счетчика, и пример ее использования на BP7. tsc.asm .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. Обратите внимание и сделайте выводы, как много времени занимает вывод на екран. Прикреплённый файлtsc.zip (0.93 Кбайт, скачиваний: 631) |
Сообщ.
#3
,
|
|
|
Если не хочется подключать модуль DOS, замерить время можно с помощью взятия значения напрямую из памяти по адресу $0046C (4 байта). Там хранится число "тиков" встроенных часов реального времени, которые тикают 18.54 раз в секунду. И именно оттуда тащат его функции модуля.
Если у вас все еще нет процессора Pentium (или есть, но нужно измерять время на процессоре ниже него), можно разогнать встроенный таймер, при этом нужно будет использовать встроенный ассемблер. Вот этот код по-моему достаточно откомментирован: 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; Значение AL в первом OUT флаговое, это байт управления таймером. Биты 7 и 6 содержат номер канала (для часов это 0), 5й и 4й управляют записью соответственно старшего и младшего байта счетчика таймера, 3,2,1 биты содержат режим работы (новый) счетчика, для часов используется режим 3 - периодические прямоугольные колебания выходного сигнала, каждый полупериод длится N/2 колебаний генератора (1193182 Гц), прерывание генерируется каждый переход с низкого на высокий уровень. 0й байт определяет формат записываемого счетчика (1 - двоично-десятичный, 0 - шестнадцатеричный). При записи обоих байтов счетчика сначала записывается младший байт. Для возврата в нормальное состояние в счетчик должен быть записан 0 в оба байта, что соответствует значению счетчика 65536 и нормальной частоте прерываний. Т.е. код для возврата: 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)); Работой счетчика (на случай выключения из анализа процедуры или двух ) можно управлять переменной int08handler_tickin, если она true, то счет идет. Вне зависимости от нее, старый вектор прерывания вызывается с нужной частотой. |
Сообщ.
#4
,
|
|
|
Не стал править предыдущий пост, он не расконвертился
Насчет таймера в памяти ДОС: При каждом срабатывании прерывания 08 происходит увеличение значения meml[0:$046C] на единицу, далее оно проверяется на достижение значения $1800B0 (в десятичной системе 1573040), которое соответствует переходу через полночь (24 часа). Если на часах полночь, ставится в единицу байт по адресу (вроде бы mem[0:$0467], точно не помню). Это значение меняется 18.2 раза в секунду (Some1 был прав) и доступно через "массив" meml любой программе, работающей под ДОС. Чтобы измерить время, нужно: 1) Определить кусок программы, для которого вам нужно узнать время его работы; 2) Сохранить значение этого участка памяти: starttime:=meml[0:$046C]; вставив этот оператор сразу перед началом куска; 3) Сразу после него считать значение еще раз: endtime:=meml[0:$046C]; Для иллюстрации я написал 2 переменные, можно обойтись и одной 4) Проверить, не было ли пОлночи в процессе работы: if endtime<starttime then inc(endtime,$1800B0); 5) Осталось взять разницу между значениями и разделить её на 18.2. Результат - время в секундах с точностью плюс-минус 0.05с. Например, для тестовых задач оно определяется вот так: 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('Правильно.'); Хотя вряд ли вы будете ребенка пытать арифметикой в полночь. |
Сообщ.
#5
,
|
|
|
{ Это только интерфейсная часть кода. Целый код модуля находится в архиве } Unit XTIMER; INTERFACE Var elapsed: Longint; { прошедшее время, в милисекундах. } Procedure ClockOn; { включает счётчик времени } Procedure ClockOff; { выключает его } Procedure PrintTime; { выводит прошедшее время } IMPLEMENTATION END. тестировалось также на Пентиумах II и III, без использования модуля CRT. Исходник модуля в архиве. Прикреплённый файлxtimer.zip (1.32 Кбайт, скачиваний: 584) |
Сообщ.
#6
,
|
|
|
А на TMT можно использовать модуль ZenTimer.
|