Подтвердить что ты не робот

Инструкция CPU Relax и примитивы С++ 11

Я заметил, что многие блокирующие алгоритмы, реализованные с использованием специфичных для ОС примитивов, таких как блокировки спинов, описанные здесь здесь (которые используют специфические для Linux атомные примитивы), часто делают использование инструкции "cpu relax". С помощью GCC это может быть достигнуто с помощью:

asm volatile("pause\n": : :"memory");

В частности, эта команда часто используется в теле while блокировок спина цикла, ожидая, пока переменная будет установлена ​​на определенное значение.

С++ 11, похоже, не предоставляет какой-либо переносной инструкции типа "cpu_relax". Есть ли причина для этого? И действительно ли оператор "пауза" выполняет что-нибудь полезное?

Edit:

Кроме того, я бы спросил: почему комитет по стандартам С++ 11 не решил включить общий std::cpu_relax() или что-то еще? Сложно ли гарантировать переносимость?

4b9b3361

Ответ 1

Инструкция PAUSE имеет значение x86. Использование единственного использования находится в цикле ожидания спин-блокировки, где:

Улучшает производительность циклов "spin-wait". При выполнении цикла "spin-wait" процессоры будут подвергаться серьезному снижению производительности при выходе из цикла, поскольку он обнаруживает возможное нарушение порядка памяти. Команда PAUSE дает подсказку процессору, что кодовая последовательность представляет собой цикл ожидания ожидания.

также:

Вставка инструкции паузы в цикле спин-зит значительно снижает потребляемую мощность процессоров.

Здесь вы помещаете эту инструкцию в цикл спин-блокировки, а также x86_64. Я не могу говорить о стандартах С++ 11, но я думаю, что для них разумно сделать вывод, что подходящее место для этой магии находится в соответствующей библиотеке... наряду со всей магией, требуемой для реализации атомистики, мьютексов и т.д..

NB: PAUSE делает не освобождение процессора, чтобы разрешить запуск другого потока. Это не "низкий уровень" pthread_yield(). (Несмотря на то, что на ядрах Intel Hyperthreaded он предотвращает зависание потока с прямыми затворами от ядра). Существенной функцией PAUSE является, по-видимому, отключение обычных оптимизаций выполнения команд и конвейерной обработки, что замедляет поток вниз (a бит), но, обнаружив, что блокировка занята, это уменьшает скорость, с которой затрагивается блокирующая переменная, так что кэш-система не избивается официантом, в то время как текущий владелец блокировки пытается справиться с реальной работой.

Обратите внимание, что примитивы, используемые для "ручных" спин-замков, мьютексов и т.д., не специфичны для ОС, а специфичны для процессора.

Я не уверен, что описал бы "ручную прокачку" спин-блокировки как "незамкнутую"!

FWIW, рекомендация Intel по спин-блокировке ( "Справочное руководство по оптимизации архитектуры Intel® 64 и IA-32" ):

  Spin_Lock:
    CMP   lockvar, 0     // Check if lock is free.
    JE    Get_lock
    PAUSE                // Short delay.
    JMP   Spin_Lock
  Get_Lock:
    MOV   EAX, 1
    XCHG  EAX, lockvar  // Try to get lock.
    CMP   EAX, 0        // Test if successful.
    JNE   Spin_Lock

Ясно, что можно написать что-то, что скомпилируется для этого, используя std::atomic_flag... или использовать pthread_spin_lock(), который на моей машине:

  pthread_spin_lock:
    lock decl (%rdi)
    jne    wait
    xor    %eax, %eax
    ret
  wait:
    pause
    cmpl   $0, (%rdi)
    jg     pthread_spin_lock
    jmp    wait

который трудно по ошибке, действительно.