Сборник по задачам и примерам Assembler

Использование счетчика меток реального времени TSC



Использование счетчика меток реального времени TSC

Счетчик меток реального времени TSC (Time Stamp Counter) — регистр, содержимое которого инкрементируется с каждым тактом процессорного ядра. Каждый раз при аппаратном сбросе (сигналом RESET) отсчет в этом счетчике начинается с нуля. Разрядность регистра обеспечивает счет без переполнения в течение сотен лет. Счетчик продолжает счет как при исполнении инструкции HLT, так и при остановке процессора по сигналу STPCLK# (для энергосбережения). Чтение счетчика обеспечивает инструкция RDTSC, установкой бита CR4.TSD ее можно сделать привилегированной (доступной лишь при CPL = 0). Чтение и запись TSC возможны также по инструкциям обращения к MSR (при CPL ¦ 0), причем запись может выполняться только в младшие 32 бита, а старшие биты при операции записи обнуляются. Присутствие счетчика TSC определяется по инструкции CPUID (ЕАХ = 1), Если в результате ее вызова бит 4 регистра EDX равен 1, то процессор поддерживает счетчик меток реального времени TSC.

Команда RDTSC (ReaD from Time Stamp Counter — чтение 64-разрядного счетчика меток реального времени TSC (Time Stamp Counter)) не имеет операндов. Машинный код этой команды — Of 31. Команда проверяет состояние второго бита регистра CR4.TSD (Time Stamp Disable — отключить счетчик меток реального времени):

  • если CR4.TSD = 0, то выполнение команды RDTSC разрешается на любом уровне привилегий;
  • если CR4. TSD = 1, то выполнение команды RDTSC разрешается только на нулевом уровне привилегий.
  • Если после данной проверки выполнение команды разрешено на текущем уровне привилегий, то выполняется сохранение значения 64-битного MSR-счетчика TSC в паре 32-битных регистров EDX: ЕАХ. Если выполнение команды запрещено, то работа команды заканчивается.

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


    На взгляд автора, применение этих макрокоманд должно подчиняться следующему алгоритму. Вызов первой макрокоманды, назовем ее profileMn, должен зафиксировать момент, относительно которого будет производиться отсчет тактов процессора, то есть в начале профилируемого участка программы. Вызов второй макрокоманды prof i I er_out должен зафиксировать момент окончания работы на этом участке программы. Необходимо иметь в виду, что это «грязное» время работы программы, по которому можно производить только приблизительную оценку ее скорости работы. Для этого есть внутренняя и внешние причины. Внутренняя причина заключается в том, что полученная величина включает время, затраченное на работу некоторых команд, составляющих тело самой макрокоманды. Этот недостаток исправить легко. Что касается внешних причин, то они объективны по отношению к программе пользователя и мешают получению истинного времени профилирования. В качестве таких внешних причин могут быть программы операционной системы, которые могут приостанавливать на время программу пользователя. Внешней причиной является и отладчик, так как при работе в нем вообще нет смысла производить оценку скорости.

    Ниже приведены макрокоманды profiler_in и profiler_out с тестовым примером для проверки их работы. Данные макрокоманды производят корректировку результата своей работы, с тем чтобы исключить обсужденные выше внутренние причины «грязного» времени работы программы. Заметим, что не всякий транслятор ассемблера «знает» о новых командах микропроцессора, в том числе и о команде RDTSC. По этой причине мы ее моделируем, инициализируя в сегменте кода 2 байта значениями машинного кода этой команды db 0fh,31h.

    :prg08_01.asm - программа демонстрации использования макрокоманд profiler_in ;и profiler out для оценки производительности фрагментов кода.

    bin_dec_fpu macro string_bin_qword:REQ. string_pack:REQ. adr_string_pack:REQ, len_ ~string_pack:REQ, ad^string^EQ. len_string:REQ

    local cycl



    макрокоманда вывода на консоль десятичного числа:



    ;на входе:

    ;string_bin_qword - адрес 64-битной ячейки (описанной dt) с преобразуемым

    :двоичным целым числом

    :string_pack - адрес 80-битной ячейки (описанной dt), в которую сохраняется

    упакованное 10-е значение

    :adr_string_pack - ячейка с адресом string_pack (описанной dd)

    :len_string_pack - длина string_pack

    :adr_string - ячейка с адресом string (описанной dd). В string помещаются

    ;символы десятичных цифр для вывода.

    :len_string - размер string (18 байт)

    :.........преобразуем bin->dec

    finit

    f 11d string_bin_qword :заносим в сопроцессор двоичное целое число fbstp string_pack извлекаем упакованное десятичное

    ¦-------------распакуем..........

    Ids si.adr_string_pack

    add si.len_string_pack-2 :на конец string_pack (18 упак. дес. цифр)

    les di.adr_string

    movcx.9 ;9 пар упакованных десятичных цифр

    xor ax,ax cycl: xor ax,ax

    std ;string_pack обрабатываем с конца

    lodsb ;в al очередные 2 упакованные десятичные цифры

    распаковываем - аИ=младшая, а1=старшая

    shi ax.4

    rol al .4

    or ах.ЗОЗОп :преобразуем в символьное представление

    xchg ah .al :ап-младшая. al-старшая

    eld :в string записываем с начала

    stosw

    loop cycl

    ;.........выводим на консоль...............................

    ' mov bx.l стандартный дескриптор - экран

    mov cx.len_string

    Ids dx.adr_string -.формируем указатель на строку string

    mov ah.40h :номер функции DOS

    int 21h :выводим

    jc exit ;переход в случае ошибки

    endm

    profileMn macro val_l:REQ

    ;val_l - ячейка памяти 64 бита (2x32) для сохранения момента :начала профилирования ("грязного") pushad сохранение всех регистров общего назначения в стеке

    db Ofh.31h;RDTSC mov val_l+4.edx

    mov va!_l.eax popad восстановление всех регистров общего назначения из стека

    endm

    profiler_out macro val_l:REQ. val_2:REQ

    :val_l - ячейка памяти 64 бита (2x32). 8 которой при входе в макрос сохранен :момент начала профилирования командой profilerjn. Далее в макросе эта ячейка :содержит результат профилирования - число тактов процессора :val_2 - ячейка памяти 64 бита (2x32). в которой сохраняется момент ("грязный") окончания профилирования



    pushad сохранение всех регистров общего назначения в стеке db 0fh,31h :RDTSC - окончание профилирования

    mov val_2+4.edx

    mov val_2,eax

    профилируем pushad и popad с учетом двух shrd pushad popad

    db 0fh.31h;RDTSC

    :теперь необходимо получить чистое время профилирования, для чего результат необходимо скорректировать (уменьшить) на количество тактов процессора, :потребное для выполнения пар команд PUSHAONPOPAD и M0V

    subeax, val_2

    jnc $+4 :учет заема из старшего разряда

    dec edx

    subedx. val_2+4 ;в edx:eax кол-ва тактов для выполнения 2-х команд ;PUSHAD\POPAD и 2-х SHRD

    mov eax,val_l

    sub val_2.eax

    mov eax.val_l+4

    jnc $+7 :учет заема из старшего разряда при выполнении предыдущего вычитания

    dec val_2+4

    sub val_2+4,eax

    ;в val_2:val_2+4 - чистое количество тактов процессора для выполнения профилируемого участка

    popad восстановление всех регистров общего назначения из стека

    :выводим

    bin_dec_fpu val_2_q. string_pack, adr_string_pack, len_string_pack. adr_string. len_string

    endm .data

    val_2 label dword val_2_q dq 0 val_l label dword

    dq 0

    :b string_pack исходное значение из val_2_q в упакованном десятичном формате string_pack dt О

    len_string_pack=$-string_pack adr_stringpack dd string_pack

    string db 18 dup (0) максимальный результат состоит из 18 десятичных цифр

    len_string=$-string adr_string dd string

    .code

    профилируем выполнение команд работы со стеком profiIer_i n val_l

    push eax

    pop eax

    profiler_out val_l. val_2 exit: :выход из программы

    Составьте тестовые примеры и «поиграйтесь» с данной программой. Обратите внимание, что при задании пустой последовательности команд между парой макросов profilerjn и profi1er_out все равно получается некоторая величина профилирования. Она постоянна, ее источник — сами команды RDTSC, которые требуют тактов процессора для своего исполнения. Эту величину можно скорректировать разными способами, но можно и не трогать, а учитывать при подведении окончательных результатов тестирования нужного вам фрагмента кода. На компьютере автора эта величина равна 52.

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

     

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