CFA LogoCFA Logo Computer
Загрузка поиска
Новости Компьютеры Прайс-лист [Новое] Прайс-лист [Б/У] Для ноутбуков Конфигуратор ПК Заказ, Оплата, Доставка Сервис объявления Драйвера Статьи Как нас найти Контакты
Новости
RSS канал новостей
То, что энтузиасты ждали так долго, наконец-то случилось, и компания NVIDIA анонсировала свой новый ...
Официальный анонс графического ускорителя GeForce GTX 1080 Ti состоялся 1 марта, и партнеры NVIDIA ...
Компания ASRock представила мировой общественности материнскую плату H110-STX MXM, которая рассчитана ...
Компания MSI в рамках серии Arsenal Gaming представляет пользователям материнские платы линейки ...
По данным наших коллег, в этом месяце компания Huawei официально представит свой новый смартфон. ...
Самое интересное
Программаторы 25 SPI FLASH Адаптеры Optibay HDD Caddy Драйвера nVidia GeForce Драйвера AMD Radeon HD Игры на DVD Сравнение видеокарт Сравнение процессоров

АРХИВ СТАТЕЙ ЖУРНАЛА «МОЙ КОМПЬЮТЕР» ЗА 2002 ГОД

Мысли о Паскале

Владислав ДЕМЬЯНИШИН nitromanit@mail.ru

(Продолжение, начало см. МК № 46, 51—52, 4, 6—7, 10, 12—13, 16—18, 22 (165, 170—171, 175, 177—178, 181, 183—184, 187—189, 193))

Спрашивали? Отвечаю…

Сначала хочу поблагодарить читателей за их письма. Есть письма разные: с благодарностью, с критикой, есть письма читателей, которым не терпится попробовать свои знания на практике, — они торопят, задают интересные вопросы и не дают расслабиться в летнюю пору, когда ласково пригревает солнце и щебечут воробьи. Что-то на лирику потянуло :-).

Спрашивает читатель Александр:

«Почему, если есть переменные W:word, L:longint и есть присваивание W:=65535, то конструкция writeln(w*32535) или L:=w*32535 дает неверный результат 33001, а конструкция writeln(65535*32535) или L:=65535*32535 возвращает верное значение 2132181225

Все дело в том, что в первом случае оба операнда (переменная W и константа) имеют один и тот же тип word, и компилятор формирует соответствующий код арифметической операции над двумя операндами типа word. И даже конструкция L:=w*32535 все равно предполагает сперва выполнение выражения, а потом уже присваивание полученного результата переменной типа longint.

Во втором случае оба операнда (непосредственные значения, т. е. константы 65535 и 32535) удовлетворяют типу word. Но так как в данном случае компилятор сначала вычисляет результат такого константного выражения, а потом уже формирует код по присваиванию полученного результата переменой, то тип полученного результата приводится к типу переменной-получателю.

Т. е. в данном случае формируется не код вычисления результата выражения, а код присваивания, так как результат можно вычислить еще на этапе компиляции.

Выходом из создавшейся ситуации может служить конструкция предварительного присваивания L:=w и выражение L:=L*32535. Вот тогда и получим заветное значение типа longint без риска потерять старшие разряды числа.

Иначе говоря, наш читатель столкнулся с ошибкой скрытого переполнения, которая не может быть обнаружена на этапе компиляции и при выполнении программы не приведет к возникновению ошибки Error 201: Range check error (ошибка при проверке границ), а значит, не даст о себе знать на этапе разработки и отладки.

Рассмотрим еще один пример:

В первом случае результат выражения $10000*$10000 выходит за пределы типа longint, оставляя в переменной L значение 0 и не вызывая ошибки. А нулевое значение, как известно, не противоречит типу word переменной W.

Во втором случае результат $1000*$10000 находится в пределах типа longint, при этом, естественно, превышает диапазон типа word, из-за чего возникает ошибка Error 201: Range check error (ошибка при проверке границ).

А теперь давайте попрактикуемся.

Снятие временных характеристик программ

Бывает чрезвычайно полезно провести оценку сравнительного быстродействия частей программы. Это может иметь большое значение для достижения приемлемой производительности программы и выявления неоптимальных участков кода. Одним словом, программисту необходим инструмент для измерения интервала времени, затрачиваемого на выполнение некоторой задачи определенным участком кода составляемой программы.

Первый метод измерения интервалов времени основан на чтении счетчика системного таймера, находящегося в области данных BIOS по адресу $0040:$006C и занимающего 4 байта памяти. Этот счетчик изменяет свои показания каждую 1/18.2 секунды, увеличиваясь на единицу.

Необходимо описать переменную, расположенную в памяти по известному адресу

Чтобы не сбить показания системных часов, не следует записывать в эту переменную что-либо. Нам лишь следует из нее читать показания системного таймера, и для этого опишем процедуру чтения этой переменной.

А вот пример использования этой процедуры:

Главное достоинство такого способа замера времени — его простота. А недостаток состоит в том, что точность замера интервалов времени ограничена 1/18.2 секунды, т. е. около 55 мсек.

Таким образом, если исследуемый процесс выполнится быстрее, чем за 55 мсек, то получим нулевой интервал времени. Чтобы избежать этого, можно задать цикл for с небольшим количеством повторений исследуемого процесса, чтобы выявить ничтожно малую величину времени, затраченного на выполнение данного процесса. При использовании этого способа переменной типа longint может хватить на измерение интервала продолжительностью до 32776 часов, а это около 4 лет. У кого хватит терпения :-)?

Если возникает необходимость осуществить паузу, то первое, что приходит на ум, — это функция delay модуля CRT.PAS, которая осуществляет задержку выполнения программы на заданное количество миллисекунд. Но каково было мое удивление, когда я смог добиться полусекундной задержки строкой delay(50000), хотя с таким параметром должна была получиться пауза в 50 секунд. А все потому, что на новых процессорах, начиная с Celeron, код этой функции работает не так, как на старых.

Поэтому хочу предложить универсальный способ осуществления задержки, который будет работать независимо от специфики процессора. Вот код необходимой процедуры задержки:

При ее применении паузу следует задавать в 18-х долях секунды, т. е. Pause(1) —55 мсек, Pause(18) — одна секунда, Pause(1092) — одна минута.

Второй метод сводится к чтению счетчика канала №0 микросхемы системного таймера, который изменяется с частотой 1193180 Гц (т. е. 1193180 раз в секунду) и позволяет добиться точности в 0.838 мксек. Это реализуется простой функцией:

т. е. читаем из порта $40 сначала младший байт, а затем старший байт двухбайтного счетчика.

Но полученное таким образом значение непригодно для использования без дополнительной обработки, так как этот счетчик непрерывно уменьшается на единицу; варьируясь в пределах 0..65535, — все из-за того, что BIOS при загрузке компьютера устанавливает коэффициент пересчета счетчика (регистр задвижки) данного канала в 65535. А нам необходимо нарастающее число, для чего нужно использовать выражение 65535-ReadTimerChipCount. Помимо этого необходимо еще к полученному значению добавить количество 1/18.2 долей секунды, умноженных на коэффициент пересчета, чтобы получить правильное значение времени. Вот функция, обеспечивающая все необходимое:

Бесспорным достоинством этого метода является его высокая точность. А недостаток заключается в том, что переменной типа longint для хранения измеренного интервала времени может хватить на 30 минут. Хотя на практике приходится замерять интервалы времени, исчисляемые несколькими секундами.

Хочу снова вернуться к проблеме, связанной со стандартной функцией delay. А что если попытаться создать аналог этой функции? Такую процедуру можно назвать этим же именем, но чтобы при использовании модуля CRT.PAS не возникало проблем, назовем ее так:

Константа k содержит число тактов микросхемы системного таймера, проходящих за одну миллисекунду. Затем этот коэффициент умножаем на количество заданных миллисекунд и добавляем результат к общему числу прошедших тактов микросхемы системного таймера. Таким образом, формируется задержка в заданное количество миллисекунд.

Определение частоты центрального процессора

Иногда возникает необходимость определить частоту процессора. В числе машинных команд имеется команда RDTSC, которая возвращает в 32-разрядных регистрах EDX:EAX количество тактов процессора, произошедших с момента его сброса. Счетчик тактов процессора является 64-разрядным, и его может хватить на 585 лет при частоте CPU 1 ГГц. При включении (сбросе) процессора счетчик тактов обнуляется. Чтобы по показаниям этого счетчика вычислить частоту процессора в МГц, надо измерить несколько интервалов времени, например, по системному таймеру (длительностью 1/18.2 c) и получить среднюю длительность в тактах процессора. Затем умножить эту величину на 18.2 (лучше умножить на 1193180 (частота таймера в Гц) и разделить на 65536 (коэффициент пересчета микросхемы таймера) — тогда получим более точное значение). Результат нужно разделить на 1000000, чтобы из Гц получить МГц.

Доступ к команде RDTSC контролируется флагом TSD в управляющем регистре CR4 процессора (если флаг сброшен, команда выполняется при любом уровне привилегий выполняемой программы, а если установлен — только при нулевом уровне привилегий). Как показала практика, в MS-DOS под Windows 98 такой метод работает нормально. Он также работает и в реальном режиме центрального процессора, т. е если загрузить машину не Windows, а обычным MS-DOS (command prompt only).

Чтобы получить значение счетчика тактов процессора, придется повозиться, так как компилятор Turbo Pascal не знает о существовании машинной команды RDTSC. Мало того, компилятор не в состоянии компилировать простые машинные команды, использующие 32-разрядные регистры. В моем представлении необходимая функция может выглядеть так:

Она возвращает количество миллионов тактов процессора, произошедших со времени включения компьютера.

Ну вот, теперь осталось составить функцию окончательного определения частоты процессора

которая возвращает количество миллионов тактов процессора, произошедших за одну секунду, что и является искомой частотой процессора в МГц. Эту функцию можно применять для машин, включенных менее 24 часов подряд, и для процессоров ниже 35 ГГц. Так что можно пока быть спокойным.

Хочу добавить, что машинная команда RDTSC доступна начиная с процессоров Pentium (5x86), во всяком случае, в руководствах по процессорам i386, i486 такая команда не упоминается.

Все эти функции для удобства можно собрать в единый модуль и назвать его, например, profiler, как это сделал я.

Литература

1. Р. Джордейн. Справочник программиста персональных компьютеров типа IBM PC, XT и AT. — М.: Финансы и статистика, 1992. – 543 с.

2. Диалоговая справочная система Norton Guide.

(Продолжение следует)

Рекомендуем ещё прочитать:






Данную страницу никто не комментировал. Вы можете стать первым.

Ваше имя:
Ваша почта:

RSS
Комментарий:
Введите символы: *
captcha
Обновить






Рейтинг@Mail.ru
Хостинг на серверах в Украине, США и Германии. © www.sector.biz.ua 2006-2015 design by Vadim Popov