Является ли пример метки времени в Intel для чтения кода ASM с использованием еще двух регистров, чем это необходимо?

Я изучаю результаты измерения производительности, используя регистр временных меток (TSR), найденный в процессорах x86. Это полезный регистр, поскольку он измеряет в монотонной единице времени, которая невосприимчива к часам изменение скорости. Очень круто.

Вот документ Intel, показывающий фрагменты ASM для надежного бенчмаркинга с использованием TSR, включая использование cpuid для синхронизации конвейера. См. Стр. 16:

http://www.intel.com/content/www/us/en/embedded/training/ia-32-ia-64-benchmark-code-execution-paper.html

Чтобы прочитать время начала, в нем говорится (я немного добавил комментарий):

__asm volatile (
    "cpuid\n\t"             // writes e[abcd]x
    "rdtsc\n\t"             // writes edx, eax
    "mov %%edx, %0\n\t" 
    "mov %%eax, %1\n\t"
    //
    :"=r" (cycles_high), "=r" (cycles_low)  // outputs
    :                                       // inputs
    :"%rax", "%rbx", "%rcx", "%rdx");       // clobber

Мне интересно, почему регистры нуля используются для принятия значений edx и eax. Почему бы не удалить movs и прочитать значение TSR прямо из edx и eax? Вот так:

__asm volatile(                                                             
    "cpuid\n\t"
    "rdtsc\n\t"
    //
    : "=d" (cycles_high), "=a" (cycles_low) // outputs
    :                                       // inputs
    : "%rbx", "%rcx");                      // clobber     

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

Я прав? Или эти MOV как-то стратегические?

(Я согласен, что вам нужны регистры царапин, чтобы прочитать время остановки, так как в этом случае порядок инструкций отменяется: у вас есть rdtscp,..., cpuid. Инструкция cpuid разрушает результат rdtscp).

Спасибо

Ответ 1

Вы правы, пример неуклюж. Обычно, если mov является первой или последней инструкцией в операторе inline-asm, вы делаете это неправильно и должны использовать ограничение, чтобы сообщить компилятору, где вы хотите, вход или выход.

Смотрите мои встроенные руководства/сборники ссылок GNU C и другие ссылки в tag wiki. ( тег wiki полон хороших материал для asm в целом тоже.)


он измеряет в монотонной единице времени, которая невосприимчива к изменению тактовой частоты.

Для профилирования часто бывает полезно иметь время в основных тактовых циклах, а не на настенных часах, чтобы результаты ваших микрообъектов не зависели от энергосбережения/турбо. Счетчики производительности могут сделать это и многое другое.

Тем не менее, если реальное время - это то, что вы хотите, rdtsc - это самый низкий способ получить его.


И снова: обсуждение в комментариях: yes cpuid существует, чтобы сериализовать, убедившись, что rdtsc и последующие инструкции не могут начаться после CPUID. Вы могли бы добавить еще один CPUID после RDTSC, но это увеличило бы накладные расходы на измерение, и я думаю, что мы получаем почти нулевой коэффициент точности/точности.

Я никогда не выяснял, когда/почему LFENCE полезен с RDTSC. В инструкции по ручному вводу говорится только, что он заказывает инструкции по загрузке с памятью. все остальные инструкции. Предположительно, у Intel-микроархитектуры более сильный заказ для LFENCE, но они оставляют официальную ISA слабее, поэтому они могут изменить ее, не нарушая совместимость, чем опубликованное руководство.

Маргарет Блум откопала эту полезную ссылку, которая говорит, что Intel SDM говорит, что LFENCE сериализует RDTSC и имеет некоторые другие вещи о том, как для сериализации вокруг RDTSC.

Ответ 2

Нет, по-видимому, нет веской причины для избыточных команд MOV в встроенной сборке. В документе сначала вводится встроенная сборка со следующим утверждением:

asm volatile (
    "RDTSC\n\t"
    "mov %%edx, %0\n\t"
    "mov %%eax, %1\n\t": "=r" (cycles_high1), "=r" (cycles_low1));

У этой очевидной проблемы, что она не сообщает компилятору, что EAX и EDX были изменены инструкцией RDTSC. В документе указывается на эту ошибку и исправляется с помощью clobbers:

asm volatile ("RDTSC\n\t"
    "mov %%edx, %0\n\t"
    "mov %%eax, %1\n\t": "=r" (cycles_high), "=r" (cycles_low)::
    "%eax", "%edx")

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

asm volatile ("RDTSC\n\t"
    : "=d" (cycles_high), "=a" (cycles_low));

Аналогично, автор, по-видимому, не знает, что существует более простая версия улучшенного оператора asm, который использует RDTSC в комбинации с CPUID, как вы демонстрируете в своем сообщении.

Обратите внимание, что автор статьи неоднократно злоупотребляет термином "IA64" для ссылки на 64-битный набор команд и архитектуры x86 (по-разному называемый x86_64, AMD64 и Intel 64). Архитектура IA-64 на самом деле является чем-то совершенно другим, это тот, который используется процессорами Intel Itaninum. Он не имеет регистров EAX или RAX и не содержит инструкции RDTSC.

Хотя на самом деле не важно, что встроенная сборка авторов сложнее, чем это должно быть, этот факт в сочетании с неправильным использованием IA64, что-то, что должно было поймать редакторов Intel, заставляет меня сомневаться в достоверности этой статьи.