Несколько команд NOP не всегда занимают больше времени, чем одна команда NOP - PullRequest
3 голосов
/ 15 октября 2019

Я синхронизирую несколько инструкций NOP и одну инструкцию NOP в C ++, используя rdtsc. Однако я не получаю увеличения количества циклов, необходимых для выполнения NOP, пропорционально количеству выполненных NOP. Я не понимаю, почему это так. Мой процессор Intel Core i7-5600U @ 2,60 ГГц.

Вот код:

#include <stdio.h>

int main() {
    unsigned long long t;

    t = __rdtsc();
    asm volatile("nop");
    t = __rdtsc() - t;
    printf("rdtsc for one NOP: %llu\n", t);

    t = __rdtsc();
    asm volatile("nop; nop; nop; nop; nop; nop; nop;");
    t = __rdtsc() - t;
    printf("rdtsc for seven NOPs: %llu\n", t);

}

Я получаю такие значения, как:

rdtsc for one NOP: 78
rdtsc for seven NOPs: 91

rdtsc for one NOP: 78
rdtsc for seven NOPs: 78

при работе без настройкисродство процессора. При настройке привязки процессора, например $ taskset -c 0 ./nop$, получаются следующие результаты:

rdtsc for one NOP: 78
rdtsc for seven NOPs: 78

rdtsc for one NOP: 130
rdtsc for seven NOPs: 169

rdtsc for one NOP: 78
rdtsc for seven NOPs: 143

Почему это так?

1 Ответ

3 голосов
/ 15 октября 2019

Ваши результаты здесь, вероятно, являются измерением шума и / или частотного масштабирования, так как вы запускаете таймер для 2-го интервала сразу после того, как printf вернется из системного вызова.

RDTSC считает контрольные циклы, а не тактовые частоты ядра, так что вы в основном просто обнаруживает частоту процессора. (Понизьте тактовую частоту ядра = больше опорных циклов для того же числа тактов ядра, чтобы выполнить две команды rdtsc). Ваши инструкции RDTSC в основном связаны друг с другом;инструкции nop ничтожно малы по сравнению с количеством мопов, которые rdtsc самостоятельно декодирует (на обычных процессорах, включая ваш Broadwell).

Также RDTSC может быть переупорядочен путем выполнения внеочередного выполнения. Не то, чтобы nop делал что-либо, чего процессор должен был бы ждать;это просто задерживает интерфейс на 0,25 или 1,75 цикла от выдачи мопов 2-го rdtsc. (На самом деле я не уверен, может ли секвенсор микрокода отправлять мопы в том же цикле, что и моп из другой инструкции. Так что, возможно, 1 или 2 цикла).

Мой ответ на Какполучить счетчик циклов ЦП в x86_64 из C ++? имеет представление о том, как работает RDTSC.


Возможно, вам понадобится инструкция pause . Он работает на холостом ходу около 100 тактов ядра на Skylake и более поздних, или ~ 5 тактов на более ранних ядрах Intel. Или прокрутить PAUSE + RDTSC . Как рассчитать время для цикла задержки asm в Linux x86? показывает, возможно, полезную спин-петлю задержки, которая спит для заданного числа счетчиков RDTSC. Вам необходимо знать эталонную тактовую частоту, чтобы сопоставить ее с наносекундами, но обычно она равна номинальной максимальной частоте не турбо тактовой частоты на процессорах Intel. например, 4008 МГц на 4,0 ГГц Skylake.

Если доступно, tpause принимает метку времени TSC в качестве времени активации. (См. Ссылку). Но пока это только маломощный Tremont.


Вставка NOP никогда не будет работать надежно на современном суперскалярном / неработающем x86 с огромными буферами переупорядочения! Современный x86 не является микроконтроллером, где вы можете вычислять итерации для вложенного цикла задержки. Если окружающий код не является узким местом на внешнем интерфейсе, OoO exec просто собирается скрыть стоимость подачи ваших NOP через конвейер.

Инструкции не имеют стоимости, которую вы можете просто добавитьдо . Чтобы смоделировать стоимость инструкции, вам необходимо знать ее задержку, количество операций ввода-вывода и какие порты выполнения для нее нужны. И любые специальные эффекты на конвейере, такие как lfence, ожидающие выхода всех предыдущих мопов до того, как последующие могут выпустить. Сколько циклов ЦП необходимо для каждой инструкции по сборке?

См. Также Какие соображения относятся к прогнозированию задержки для операций на современных суперскалярных процессорах и как их можно вычислить вручную?


Обратите внимание, что желаемое время "ожидания" в ~ 100 нс не обязательно даже достаточно долго, чтобы истощить буфер выполнения не по порядку (ROB), если в полете есть пропуски кешаили, возможно, даже очень медленная цепочка зависимостей ALU. (Последнее маловероятно вне искусственных случаев). Так что вы, вероятно, не хотите делать что-то вроде lfence.

...