Lazarus и Free Pascal: как измерить точное время выполнения операции?
Несколько лет назад я описывал четыре способа измерения точного времени выполнения операции в Delphi. Сегодня рассмотрим этот же вопрос, но уже применительно к Lazarus и Free Pascal. Учитывая, что Lazarus можно использовать как в Windows, так и в Linux, способы того, как измерить точное время выполнения операции в Lazarus и Delphi будут немного различаться. Для начала, определимся с тем, время выполнения какой операции мы будем измерять. Путь это будет вот такая простенькая процедура:
Учитывая то, что на данный момент я могу проверить работоспособность того или иного способа только на двух платформах: Windows и Linux, ниже я буду давать описание применимости различных способов точного измерения времени, исходя из проверок именно на этих платформах.
Способ №1 – использование функции Now()
Платформы: Windows, Linux.
Как и в случае с Delphi этот способ вполне подойдет для измерения времени, например, следующим образом:
И, хотя в справке Free Pascal ничего не сказано про точность функции Now() (в справке Delphi написано, что точность составляет порядка секунды), я не стал рисковать и все также использовал функцию SecondsBetween() из модуля DateUtils для расчёта количества секунд, пошедших на выполнение операции.
Способ №2 – использование функции GetTickCount
Платформы: Windows, Linux
В настоящее время, в модуле sysutils Free Pascal содержатся две функции, одна из которых помечена как deprecated:
Как и в случае с Delphi, используя GetTickCount можно получить время в миллисекундах, затраченное на выполнение операции. Однако, в справке Free Pascal про эту функцию сказано следующее: функция полезна для измерения времени, но не следует делать никаких предположений относительно интервала между тиками. В принципе, примерно тоже сказано и в справке Windows относительно их функций GetTickCount.
Стоит отметить, что в зависимости от платформы на которой собирается приложение, GetTickCount64 имеет различные реализации. Так, например, если мы собираем приложение под Windows, то GetTickCount64 будет использовать одноименную функцию из модуля Windows:
Если же мы используем Linux, то реализация GetTickCount64 основывается на использовании clock_gettime() – функции из языка Си:
Способ №3 – использование функций QueryPerformanceCounter и QueryPerformanceFrequency
Платформы: Windows
Использовать функции можно, подключив в uses модуль windows. Соответственно, под Linux метод использовать невозможно, а посмотреть реализацию способа в Windows можно в предыдущей статье про точное измерение времени выполнения операций.
Способ №4 – когда разбираться с функциями лень. Используем компонент TEpikTimer
По аналогии с классом TStopwatch из Delphi, компонент TEpikTimer дает нам широкие возможности по определению времени, затраченного на выполнение какой-либо операции. В принципе, этим компонентом я обычно и пользуюсь.
Чтобы установить компонент, заходим в меню “Пакет – Установить/удалить пакеты”
В списке “Доступные для установки” ищем пакет под названием etpackage_dsgn
Жмем кнопку “Установить выбранное” под списком и, потом, “Сохранить и перезапустить IDE”. В новом окне жмем “Продолжить”, Lazarus пересоберется и на вкладке System появится новый компонент TEpikTimer:
У компонента есть множество полезных функций, позволяющих измерить точное время выполнения операции в Lazarus, я продемонстрирую только одну из них.
Здесь я вывел время выполнения операции в секундах. Также EpikTimer умеет выводить время в различных форматах.
Помимо представленных выше способов, вы можете также воспользоваться и другими способами измерения точного времени выполнения операции в Lazarus и Free Pascal, например компонентом TJclCounter от JEDI, однако, в любом случае, эти компоненты будут использовать один из способов, рассмотренных выше, как это делает EpikTimer.
Процедура TextColor назначает цвет текста, а оператор Write выводит текст на экран:
TextColor(LightGreen); WriteLn(‘Нажмите Enter чтобы запустить секундомер’); WriteLn(‘Нажмите повторно, чтобы остановить’); Write(‘Нажмите ещё раз, чтобы запустить заново’); Окончание Ln осуществляет переход на следующую строку.
Оператор ReadLn вводит значения с клавиатуры, но в даном случае он просто ждёт, когда пользователь нажмёт Enter:
Делаем бесконечный цикл:
while (true) do begin
Конструкция while (true) do begin переводится как: Пока (условие) делай(). Спрашивается зачем здесь begin? В данном случае нам нужен составной оператор, а это значит что пока условие истинно выполняется несколько операторов. Если бы здесь не стоял begin то после While выполнялся только один оператор, что привело бы неправильной работе программы. Чтобы закончить оператор While в конце мы напишем end.
Теперь обнулим счётчик:
Следующий оператор переводится как: пока не нажата клавиша делай().
while not keypressed do begin Оператор ClrScr очищает экран:
Ставим условим: если секунд больше 60 и меньше 3600(это нужно для того, чтобы когда времени было больше часа, программа печатала только то что находится после третьего if) то:
Итак, программа написала что прошло 0 секунд, теперь увеличивает счётчик i на 10 миллисекунд и так как программа выполняет всё мгновенно делаем задержку на это же время:
Далее делаем конец для оператора While (not keypressed):
end; Если пользователь нажал клавишу Enter, то программа ждёт когда когда он опять её нажмёт, чтобы запустить секундомер заново:
Неслучайно мы поставили обнуление счётчика после оператора While (true), потому что когда пользователь второй раз нажмёт Enter, программа пойдёт как раз с него, обнулит счётчик и начнёт отсчет заново.
Далее делаем конец для While и для всей программы:
Создать игру, Конструкторы игр, Игровые движки, Разработка игр, Игровые ресурсы
10 Лучших пользователей
/—>
Партнеры сайта
.
Пишем модуль для работы с системным таймером
Ну и не надо! Мы ведь не чайники? Конечно, не чайники! Сами напишем. При написании программ последовательный код стараются обединить в цыклы. Код, повторяющийся в програме выносят в отдельные процедуры и функции. А код, который явно будет использоватся не в одной программе, выносят в модули. Мы так и сделаем. Давайте создадим в Паскале файл TIMER.PAS и начнем. Как известно название модуля и файла должны совпадать, поетому пишем:
Далее необходимо создать интерфейсную часть модуля. Тут давайте остановимся и разберемся что нам нужно. Во-первых нам нужны средства для измерения времени исполнения кода. Во-вторых средства по остановке программы на определенное время. Кроме того, при остановке может, понадобится вывод времени, которое прошло.
Далее следует самое интересное. Вы еще не задумывались каким же способом мы будем производить замер времени? А почему бы не использавать аппаратный таймер? Темболее это очень просто:
procedure Start (var T:longint); begin T:=SystemTimer; end; procedure Stop (var T:longint); begin T:=SystemTimer-T; end;
procedure Pause (T:longint; Show:boolean); var Xn,Xt:longint; begin Xt:=0; Xn:=SystemTimer; While ((Xt-Xn)/18.2)*1000
Дата добавления: 2014-11-27 ; просмотров: 881 ; Нарушение авторских прав
Постановка задачи. Напишите программу «Таймер». По заданному интервалу времени программа должна вести обратный отсчет до нуля. По нажатию кнопки «Пуск» таймер запускается, и на экране видны «тикающие часы», а надпись на кнопке сменяется на «Стоп». Таймер можно остановить, нажав на «Стоп».
Реализация. На форму положите два компонента TEdit для ввода минут и секунд, кнопку TButton с названием «Пуск», три надписи TLabel и таймер TTimer. Компонент TTimer невидим во время работы программы, поэтому его можно положить на любое место формы. В свойстве Timer1.Interval задается интервал времени, через который работает код, написанный в процедуре Timer1Timer (Sender : TObject). Интервал времени задается в миллисекундах. Если Timer1.Interval = 1000, то временной промежуток равен 1 сек. Таймер включен, если его свойство Enabled имеет значение true и выключен, если false. Если таймер выключен, то код в процедуре Timer1Timer(Sender: TObject) не работает.
Вид формы на этапе разработки приведен на рис. 3. Работающее окно изображено на рис. 4.
Рис. 3. Форма «Таймер» Рис. 4. Вид работающего окна
И так такт таймера по умолчанию составляет 55 мс. Но в жизни есть такие чудесные моменты, когда требуется, что-нибудь периодически выполнять через 20 мс, 10 мс или даже 1 мс. В сети выложены примеры быстрых таймеров, но, честно говоря, что к чему в них я так понять и не смог. Из не проверенных данных я знаю, что изменить такт таймера с 55 мс на 10 мс условно говоря просто, вот только ни где не сказано как это сделать. Как получают более мелкие такты для меня большая загадка. Просветите, пожалуйста.
Примеры перепрограммирования частот системного таймера находятся здесь: http://pascal.sources.ru/datetime/index.htm
PS Я не программист, так балуюсь, С и Асм’у не обучен.
Ищущий истину
Группа: Пользователи Сообщений: 4 825 Пол: Мужской Реальное имя: Олег
Репутация: 45
Ищущий истину
Группа: Пользователи Сообщений: 4 825 Пол: Мужской Реальное имя: Олег
Репутация: 45
Ищущий истину
Группа: Пользователи Сообщений: 4 825 Пол: Мужской Реальное имя: Олег
Репутация: 45
Ищущий истину
Группа: Пользователи Сообщений: 4 825 Пол: Мужской Реальное имя: Олег
Репутация: 45
Важные выдержки оттуда:
Таймеру соответствуют четыре порта ввода/вывода со следующими адресами:
Поле M определяет режимы работы микросхемы 8254:
Поле SC определяет номер канала, для которого предназначено управляющее слово. Если в этом поле задано значение 11, будет выполняться чтение состояния канала.
Приведем формат команды RBC чтения слова состояния канала:
С помощью этой команды вы можете выполнять операции чтения состояния каналов либо запоминание регистра счетчика CE каналов. Можно выполнять эти операции как для отдельных каналов, так и для всех каналов одновременно, если установить соответствующие биты (1, 2, 3) в 1.
Формат слова состояния канала напоминает формат регистра управляющего слова, за исключением двух старших разрядов 7 и 6:
Разряд FN используется, в основном, в режимах 1 и 5 для определения, произошла ли загрузка константы из регистра CR в регистр счетчика CE.
Разряд OUT позволяет определить состояние выходной линии канала OUT в момент выполнения команды RBC.
Для программирования канала таймера необходимо выполнить следующую последовательность действий:
вывести в порт управляющего регистра с адресом 43h управляющее слово; требуемое значение счетчика посылается в порт канала (адреса 40h. 42h), причем вначале выводится младший, а затем старший байты значения счетчика. Сразу после этого канал таймера начнет выполнять требуемую функцию.
Для чтения текущего содержимого счетчика CE необходимо выполнить следующее:
вывести в порт управляющего регистра код команды CLC (команда запоминания содержимого регистра CE); вывести в порт управляющего регистра код команды запроса на чтение/запись в регистры канала (поле RW должно содержать 11); двумя последовательными командами ввода из порта нужного канала ввести младший и старший байты текущего сосотояния счетчика CE. Для чего вам может понадобиться перепрограммирование каналов таймера?
Если вам надо повысить точность измерения времени, выполняемого с помощью канала 0 таймера, вы можете увеличить частоту генерируемых этим каналом импульсов (стандартно 18,2 Гц). По окончании измерений режим работы канала необходимо восстановить для правильного функционирования системы.
ЗЫ Попойму у Vesper’a так же опечатка в примере програмы:
Ищущий истину
Группа: Пользователи Сообщений: 4 825 Пол: Мужской Реальное имя: Олег
Репутация: 45
Системный тактовый генератор работает на частоте 1,19МГц. Так что если таймер запрограммирован на 10000 импульсов, он будет выдавать сигналы примерно 100 раз в секунду. Можно добиться любой частоты импульсов на выходе таймера, подбирая соответствующий делите� �ь. После загрузки делителя схемы динамика запускаются для работы под управлением таймера, после чего выходные сигналы таймера будут управлять частотой формируемого звука, а� �компьютер может выполнять любую другую работу.
Такой способ управления динамиком позволяет программам работать, пока динамик воспроизводит звук. Таким образом организована работа одной из процедур Бейсика, фоновая музыка или MB. Необходимо заметить, что при формировании звука с помощью таймера, он продолжает звучать до тех пор, пока он не будет отключен той же программой, которая его запустила.
Листинг 3.2. может использоваться в качестве примера формирования звуков без использования таймера. Программа на ассемблере, представленная листингом 11.1, показывает, как запустить звучание динамика с помощью таймера.
Е-е-е, получилось. Значиться так:
В паскалевском виде это выглядит так: Port [$43]:=38;
И потом вводим максимальное значение для младшего байта 256: Port [$40]:=256;
Менее шустный таймер у меня пока не получается :p2: – машина виснет. Но ковырять надо RW.
Ищущий истину
Группа: Пользователи Сообщений: 4 825 Пол: Мужской Реальное имя: Олег
Репутация: 45
Чавой-то я сомневаюсь
Ищущий истину
Группа: Пользователи Сообщений: 4 825 Пол: Мужской Реальное имя: Олег
Репутация: 45
Пишем модуль для работы с системным таймером
Часто при программировании в некоторых местах программы необходимо замерять время исполнения кода, в других просто останавливать выполнение не некоторое время. Например, если писать игру, необходимо создавать код, который бы ограничивал скорость игры. Конечно, если игра очень тяжелая, то некоторое время она может существовать без такого ограничителя. Но со временем вычислительная мощь компьютеров растет (к сожалению не сама по себе) и в игры без ограничителя скорости играть становится невозможно. Или вы решили написать бенчмарк для процессора. Тут уже нужны очень точные средства для замера времени исполнения кода. Таких примеров можно привести уйму. Проще сказать, что в любой более-менее серьезной программе измерение времени просто необходимо. К сожалению штатные средства в Паскале ограничиваются только процедурой Delay что описана в модуле CRT. Но она очень сильно зависит от производительности системы. Конечно, можно использовать процедуру GetTime, но она довольно громоздка. А стандартных процедур по замеру времени выполнения кода вообще нет.
Ну и не надо! Мы ведь не чайники? Конечно, не чайники! Сами напишем. При написании программ последовательный код стараются объединить в циклы. Код, повторяющийся в программе выносят в отдельные процедуры и функции. А код, который явно будет использоваться не в одной программе, выносят в модули. Мы так и сделаем. Давайте создадим в Паскале файл TIMER.PAS и начнем. Как известно название модуля и файла должны совпадать, поэтому пишем:
Далее необходимо создать интерфейсную часть модуля. Тут давайте остановимся и разберемся что нам нужно. Во-первых нам нужны средства для измерения времени исполнения кода. Во-вторых средства по остановке программы на определенное время. Кроме того, при остановке может, понадобится вывод времени, которое прошло.
Данная программа демонстрирует возможности модуля Timer. В начале она исполняет цикл от 1 до 30000 в котором высчитывает значение а. Время выполнения этого цыкла и замеряют наши процедуры Start и Stop. После чего, дождавшись нажатия на Enter делаем паузу на 10.000 секунд с разрешаем процедуре Pause осуществлять вывод на экран.
Теперь вы сможете использовать точный таймер в своих программах. А почему же я не воспользовался процедурой GetTime? Только из-за ее громоздкости? Конечно нет. Посмотрите на код. Что мы собственно использовали? Только прямой доступ к физическому адресу аппаратного таймера. Так кто мешает использовать его в других языках программирования? Вот тут то и оно.
1193180 Гц, получаем указанные выше формулы и диапазоны частоты и периода.
Напрямую таймер в среде Windows не программируется, поскольку ОС монопольно использует его для переключения нитей. Тем не менее, Windows эмулирует в DOS-приложениях таймер и IRQ0 (правда, для некоторого «разумного» диапазона частот).
А как переделать Вашу программу, если нужно еще в 4 раза быстрее (т.е. в 16 раз быстрее стандартных 18 Гц)?
И не повлияют ли дополнительные прерывания на системное время?
Короче, я рекомендую тебе вместо реанимирования давно умерших тем начать НОВУЮ ТЕМУ в разделе 32-битные компиляторы. И в ней дать все детали твоего софта.
Новичок
Группа: Пользователи Сообщений: 13 Пол: Женский
Репутация: 0
Я работаю, как ни странно, под DOS32 (Free Pascal). Это связано с тем, что программы были написаны мною для научных целей довольно давно, а переделывать их под Дельфи влом. Кроме того, Винда жрет немало памяти, что для моих программ критично (2 гига нужны полностью), да и с устойчивостью под Виндой иногда возникают проблемы.
Что касается многоядерности, я просто не знаю, умеет ли ее реально использовать Free Pascal. Плюс нет уверенности, что при компиляции программ в многоядерном режиме они будут работать устойчиво.
В общем, DOS (в расширении 32 bit) IMHO еще рано хоронить.