Почему nop не занимает один такт

Я написал базовый код, чтобы узнать количество тактов, используемых nop. Мы знаем, что nop занимает один такт.

#include <stdio.h>
#include <string.h>
#include <stdint.h>


int main(void)
{
    uint32_t low1, low2, high1, high2;
    uint64_t timestamp1, timestamp2;
    asm volatile ("rdtsc" : "=a"(low1), "=d"(high1));
    asm("nop");
    asm volatile ("rdtsc" : "=a"(low2), "=d"(high2));
    timestamp1 = ((uint64_t)high1 << 32) | low1; 
    timestamp2 = ((uint64_t)high2 << 32) | low2; 
    printf("Diff:%lu\n", timestamp2 - timestamp1);
    return 0;
}

Но выход не 1.

Иногда это 14 или 16.

Могу ли я узнать причину этого. я что-то пропустил


person md.jamal    schedule 12.01.2020    source источник
comment
Во-первых, ваш цикл синхронизации также включает время для выполнения 1 rdtsc инструкции.   -  person 1201ProgramAlarm    schedule 12.01.2020
comment
Какие параметры компилятора вы используете?   -  person Sedat Kapanoglu    schedule 12.01.2020
comment
Обычный: gcc userprog.c -o userprog   -  person md.jamal    schedule 12.01.2020
comment
Флаг constant_tsc в /proc/cpuinfo указывает на то, что вы измеряете время, а не циклы. Вы, вероятно, захотите отправить инструкцию сериализации, чтобы предотвратить выполнение не по порядку. Вы установили привязку процессора к одному ядру?   -  person LegendofPedro    schedule 12.01.2020
comment
Откуда вы знаете, что nop занимает один цикл? Некоторые процессоры могут удалять несколько команд из потока команд в каждом цикле, поэтому они никогда не отправляются и не требуют времени выполнения.   -  person Eric Postpischil    schedule 12.01.2020
comment
Вы, вероятно, захотите скомпилировать с -O0, чтобы отключить оптимизацию (и, возможно, -S, чтобы проверить вывод сборки).   -  person LegendofPedro    schedule 12.01.2020
comment
@LegendofPedro: нет, -O0 просто поместит больше мусора в интервал времени, но все равно не заставит 2-й RDTSC ждать завершения более ранних инструкций. Или также остановите запуск первого RDTSC раньше времени. См. мой канонический ответ о RDTSC: Как получить количество циклов ЦП в x86_64 из C++?   -  person Peter Cordes    schedule 12.01.2020
comment
Мы знаем, что nop занимает один такт. Что за чип? вы попали туда, Dorito? А если серьезно, то на каком процессоре вы тестировали это, просто чтобы ответ мог включать подробности в объяснение базовой / эталонной частоты (TSC) по сравнению с фактической частотой ядра циклов, предполагая constant_tsc? Конечно, не 486 или ранее, где NOP фактически стоил бы 1 цикл.   -  person Peter Cordes    schedule 12.01.2020
comment
@PeterCordes, это интересно, я ожидаю, что оптимизация ничего не добавит (или не сделает многого) к встроенному asm, за исключением, возможно, удаления nop.   -  person LegendofPedro    schedule 12.01.2020
comment
@LegendofPedro: Точно, вам нужен оптимизированный ассемблер. И нет, GCC/clang не понимает ассемблерный шаблон, они только сканируют его на наличие "%number" подстановок операндов, прежде чем передать результат (включая сгенерированный компилятором ассемблер) ассемблеру. С -O0 вы получите места в стеке для low1 и low2, а не просто mov в другие регистры (или, может быть, shl/lea в другой регистр до 2-го rdtsc). При втором взгляде вы на самом деле не получите больше инструкций в (попытке) временной области от -O0, потому что нечего разливать / перезагружать; нет входов для 2-го asm   -  person Peter Cordes    schedule 12.01.2020


Ответы (1)


Мы знаем, что nop занимает один такт.

Современный ЦП можно рассматривать как конвейер стадий; где передняя часть может получать и декодировать несколько инструкций параллельно и помещать полученные микрооперации в буфер, где они ждут, пока их зависимости будут удовлетворены (прежде чем они будут приняты исполнительным блоком, где несколько микроопераций могут выполняться одновременно). одновременно несколькими исполнительными устройствами).

У NOP нет микроопераций — он просто отбрасывается внешним интерфейсом. Это не стоит 1 цикл.

Но выход не 1.

Вероятно, требуется 14 или 16 циклов для инструкций, которые компилятор генерирует для обработки выходных данных первого rdtsc, затем подготавливает все для второго rdtsc, а затем сам второй rdtsc.

Обратите внимание, что rdtsc, вероятно, подсчитывает циклы таймера с фиксированной частотой, который не имеет ничего общего с текущей (переменной) тактовой частотой ЦП; поэтому 14 или 16 «временных циклов» могут быть (например) 7 или 8 циклами ЦП.

person Brendan    schedule 12.01.2020
comment
14 циклов на самом деле мало для последовательного rdtsc (только с mov ecx, eax между ними). Обратите внимание, что rdtsc не ожидает завершения выполнения предыдущих инструкций, прежде чем он выполнится, и у него нет входных данных, поэтому его микрокод может начать выполнение, как только появится свободная исполнительная единица. - person Peter Cordes; 12.01.2020
comment
Пропускная способность Skylake RDTSC составляет один процессор на 24 такта ядра (agner.og/optimize), а Ryzen — 36 тактов. Таким образом, ЦП OP предположительно значительно превышает эталонную частоту TSC. Если только это не K8 на холостом ходу, хотя у K8, вероятно, нет constant_tsc. В любом случае см. также Как получить количество циклов ЦП в x86_64 из C++? для получения более подробной информации о rdtsc - person Peter Cordes; 12.01.2020
comment
Re: стоимость nop: ничего не стоит, если у вас нет узких мест на интерфейсе, иначе это может увеличить общую стоимость декодирования + выдачи группы инструкций на 1/4 или 1/5 цикла, или больше, если это вызывает проблемы с другим выравниванием. Однако на самом деле интерфейс не отбрасывает его; он занимает место в ROB (1 uop слитого домена), но не нуждается в исполнительном блоке (0 без слияния). Вы можете думать об этом как об интерфейсе, вставляющем его в серверную часть в уже выполненном состоянии, как устранение mov и (в семействе Sandybridge) xor-zero. - person Peter Cordes; 12.01.2020
comment
Выполнение NOP не является достаточно важным вопросом производительности, чтобы его стоило специально записывать раньше во внешнем интерфейсе, чтобы сохранить пропускную способность интерфейса. Я предполагаю, что на самом деле это усложнит кучу крайних случаев, даже если мы согласимся с тем, что счетчики производительности больше не будут учитывать это. - person Peter Cordes; 12.01.2020
comment
Constant_tsc присутствует в /proc/cpuinfo. Я запускаю это на vmware - person md.jamal; 12.01.2020