Моя спин-блокировка, очевидно, имеет цикл занятости, в то время как блокировку не удается получить:
while(try_lock() == false)
{
// Use _mm_pause() or _tpause() here?
}
Я заметил, что у меня нет _mm_pause()
внутри цикла. Я понимаю, что пропуск этого параметра может привести к снижению производительности в отношении барьеров/ограждений/упорядочения памяти?
Прежде чем добавить _mm_pause()
, я обнаружил _tpause()
:
https://www.felixcloutier.com/x86/tpause
Однако из руководства Intel Intrinsics Guide его использование кажется немного более сложным.
Я хотел бы максимизировать производительность/не беспокоиться о энергопотреблении.
Какой мне следует использовать, и если это _t_pause()
, то как его правильно использовать? Я не могу найти ни одного примера использования, даже на Github.
Архитектура будет представлять собой модели Intel Xeon 2022+.
Обновлено:
Я только что заметил _mm_pause()
задержка 140 циклов?!
К сожалению, я не заметил задержки для _tpause()
.
Хотя 140 циклов кажется много. Следует иметь в виду, что задержка при перемещении строки кэша с одного ядра на другое также значительна, особенно на серверах Xeon. Минимум несколько десятков циклов (обязательно 25-90 циклов).
Да, и имейте в виду, что спин-блокировка может быть очень вредной (например, взаимоблокировка из-за инверсии приоритета) даже для производительности, включая задержку (например, ожидание очень длительного такта, когда поток, снимающий блокировку, не запланирован). Как правило, это очень плохая идея, если у вас нет полного контроля над машиной (например, привязка потоков, запуск других приложений, настройка ядра и т. д.) и вы точно не знаете, что делаете. Они являются очень распространенным источником недетерминированных скрытых взаимоблокировок и проблем с производительностью (хотя в первую очередь они должны улучшать производительность)...
В процессоре с гиперпоточностью tpause
может более полно передать физическое ядро другому логическому ядру на более длительный срок. Так что, возможно, задержка будет хуже, но, возможно, и пропускная способность лучше, по крайней мере, если другое ядро делает что-то полезное, а не ожидает вращения.
Из этого патча для Linux:
/*
* On Intel the TPAUSE instruction waits until any of:
* 1) the TSC counter exceeds the value provided in EDX:EAX
* 2) global timeout in IA32_UMWAIT_CONTROL is exceeded
* 3) an external interrupt occurs
*/
Похоже, TPAUSE
предназначен для режима сна с оптимизацией энергопотребления, а не для вращения с малой задержкой. Для этого вам следует использовать PAUSE
.
Кроме того, PAUSE
задержка и поведение сильно зависят от микроархитектуры, поэтому вам следует проверить/сравнить его с реальной целью. Если у вас процессор Xeon 2022+, то это маловероятно на микроархитектуре Skylake (которая была представлена примерно в 2015 году).
«Я понимаю, что пропуск этого параметра может привести к снижению производительности в отношении барьеров/ограждений/упорядочения памяти?» да, потому что строка кэша извлекается потоком часто без причины и без паузы. Это вызывает конфликт в строке кэша (и, конечно же, приводит к перенасыщению сети L3 мусором). Это может быть очень критично, если строка кэша используется не только для спин-блокировки (ложное совместное использование + конкуренция). Наилучшее время зависит от количества ядер, обращающихся к одной и той же строке кэша, задержки кэша L3 и вероятности разблокировки спин-блокировки.