Delphi 3. Библиотека программиста

       

Использование RDTSC для измерения временных интервалов на Pentium


В доисторическую эпоху написание быстрых программ не сводилось к правильному выбору алгоритма; программисту приходилось помнить временные характеристики различных команд и измерять время выполнения различных вариантов. Поскольку системный таймер «тикает» лишь каждые 55миллисекунд, при измерениях приходилось повторять одни и те же вычисления сотни тысяч раз или же пускаться на хакерские ухищрения вроде чтения внутренних регистров таймера, чтобы получить значение времени с точностью до 838 наносекунд.

В наши дни появились хорошие компиляторы и быстрые процессоры, в результате чего стало довольно трудно написать какой-нибудь «предельно тупой» код, существенно замедляющий работу программы. Однако по иронии судьбы средство для измерения временных интервалов появилось лишь в процессоре Pentium. Команда RDTSC (Read Time Stamp Counter) возвращает количество тактов, прошедших с момента подачи напряжения или сброса процессора. Где была эта команда, когда мы действительно нуждались в ней?

И все же лучше поздно, чем никогда. Команда RDTSC состоит из двух байтов: $0F 31. Она возвращает в регистрах EDX:EAX 64-битное значение счетчика. Поскольку сопроцессорный тип данных comp представляет собой 64-битное целое, мы можем прочитать текущее значение с помощью кода Delphi, приведенного в листинге 9.3.

Листинг 9.3. RDTSC.SRC

const D32 = $66; function RDTSC: comp; var TimeStamp: record case byte of 1: (Whole: comp); 2: (Lo, Hi: LongInt); end; begin asm db $0F; db $31; // BASM не поддерживает команду RDTSC {$ifdef Cpu386} mov [TimeStamp.Lo],eax // младшее двойное слово mov [TimeStamp.Hi],edx // старшее двойное слово {$else} db D32 mov word ptr TimeStamp.Lo,AX {mov [TimeStamp.Lo],eax - младшее двойное слово} db D32 mov word ptr TimeStamp.Hi,DX {mov [TimeStamp.Hi],edx - старшее двойное слово} {$endif} end; Result := TimeStamp.Whole; end;

Одна из проблем, с которой вы столкнетесь при использовании команды RDTSC, заключается в том, что функции IntToStr и Format('%d') могут работать только со значениями типа LongInt, а не comp. Если этим функциям передается значение типа comp, оно не может превышать High(LongInt), то есть 2147483647. Возможно, эти цифры производят впечатление, если они определяют сумму в долларах, но на Pentium с тактовой частотой 133 МГц это соответствует всего лишь 16 секундам. Если вам потребуется сравнить время работы двух длительных процессов, разность между показаниями таймера в начале и конце работы легко может превысить High(LongInt).

Проблема решается просто. Хотя тип comp соответствует 64-битному целому, на самом деле это тип данных сопроцессора 80х87. Чтобы отформатировать comp функцией Format(), необходимо воспользоваться форматами с плавающей точкой. Функция CompToStr в листинге 9.4 скрывает все хлопотные подробности, причем с ней сгенерированный компилятором объектный код получается более компактным, нежели при непосредственном использовании нескольких вызовов Format().

Листинг 9.4. COMP2STR.SRC

function CompToStr(N: comp): string; begin Result := Format('%.0n', [N]); end;

Напоследок скажу лишь следующее. Потребность в измерении временных интервалов сейчас возникает намного реже, чем в былые времена. В то же время с появлением команды RDTSC такое измерение становится удобным и надежным.

На этом замечании я передаю повествование своему соавтору, Эду Джордану. Продолжай, Эд!



Содержание раздела