Почему у AMD-CPU такой глупый PAUSE-тайминг
Я разработал объект-монитор, подобный объекту Java для C++, с некоторыми улучшениями. Основное улучшение состоит в том, что есть не только спин-цикл для блокировки и разблокировки, но и для ожидания события. В этом случае вам не нужно блокировать мьютекс, но нужно указать предикат для функции wait_poll, и код неоднократно пытается заблокировать опрос мьютекса, и если он может заблокировать мьютекс, он вызывает предикат, который возвращает (или перемещает) пару логического типа и типа результата.
Ожидание семафора и / или объекта-события (Win32) в ядре может легко занять от 1.000 до 10.000 тактов, даже если вызов немедленно возвращается, потому что семафор или событие были установлены ранее. Таким образом, должен быть счетчик вращений с разумной связью с этим интервалом ожидания, например, вращение одной десятой минимального интервала, затрачиваемого в ядре.
С моим объектом-монитором я взял алгоритм пересчета spincount из glibc. И я тоже использую PAUSE-инструкцию. Но я обнаружил, что на моем процессоре (TR 3900X) команда паузы слишком быстрая. В среднем это около 0,78 нс. На процессорах Intel это намного более разумно - около 30 нс.
Это код:
#include <iostream>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <immintrin.h>
using namespace std;
using namespace chrono;
int main( int argc, char **argv )
{
static uint64_t const PAUSE_ROUNDS = 1'000'000'000;
auto start = high_resolution_clock::now();
for( uint64_t i = PAUSE_ROUNDS; i; --i )
_mm_pause();
double ns = (int64_t)duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count() / (double)PAUSE_ROUNDS;
cout << ns << endl;
}
Почему AMD взяла такой глупый тайминг ПАУЗЫ? PAUSE предназначена для циклов ожидания и вращения и должна точно соответствовать времени, которое требуется для того, чтобы содержимое строки кэша переключилось на другое ядро и обратно.
1 ответ
Но я обнаружил, что на моем процессоре (TR 3900X) инструкция паузы выполняется слишком быстро. В среднем это около 0,78 нс. На процессорах Intel это намного разумнее, около 30 нс.
Инструкция никогда не имела ничего общего со временем и не предназначена для использования в качестве временной задержки.
Это нужно для того, чтобы ЦП не тратил свои ресурсы (спекулятивно) на параллельное выполнение множества итераций цикла; что особенно полезно в ситуациях с гиперпоточностью, когда другой логический процессор в ядре может использовать эти ресурсы, но также полезно для сокращения времени, необходимого для выхода из цикла при изменении условия (поскольку у вас нет «N итераций» инструкций, поставленных в очередь до изменения условия).
Учитывая это; для чрезвычайно сложного ЦП, который может одновременно выполнять 200 инструкций, само по себе может произойти мгновенно, но вызвать на своем пути конвейерный пузырь «длительностью 200 циклов»; и для чрезвычайно простого процессора («в порядке» без спекулятивного выполнения) может/должен буквально ничего не делать (рассматривается как
PAUSE предназначена для циклов ожидания и ожидания и должна точно соответствовать времени, которое требуется содержимому кэш-линии для перехода к другому ядру и обратно.
Нет. Предположим, что строка кеша находится в «модифицированном» состоянии в кеше другого процессора, а инструкция после выглядит примерно так:
Примечание. Если вам действительно нужна крошечная временная задержка, вам следует взглянуть на