Циклы процессора, проведенные в состоянии ожидания спин-блокировки

Пока у меня есть хороший спинлок, который работает как намерение:

    std::atomic_flag barrier = ATOMIC_FLAG_INIT;

inline void lock( ){
while( barrier
.test_and_set( std::memory_order_acquire ) )
{}
}

Однако я хочу знать (ориентировочно), сколько циклов ЦП тратится в нем (если ожидание занятости слишком велико, возможно, я рассмотрю мьютекс, который по крайней мере переводит ожидающие потоки в спящий режим):

    inline void lock( int & waitCounter){
while( barrier
.test_and_set( std::memory_order_acquire ) )
waitCounter++;
}

Конечно, это не учитывает саму инструкцию блокировки, поэтому на какую константу я должен увеличивать waitCounter, чтобы получить точное представление о циклах, проведенных в занятом ожидании (я считаю, что инструкции не будут переданы по конвейеру из-за барьера памяти, поэтому счет довольно точный теоретически)?

waitCounter+=2;
waitCounter+=3;
waitCounter+=4; //...

1

Решение

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

Я недавно проверил это, Вот.

Короткий ответ: он может сильно различаться из-за того, что вы можете контролировать напрямую (код приложения), а чего нет (конфликт из-за шины). Соотношение между наименьшим числом циклов и наибольшим может составлять от 110 до 950 или больше.

1

Другие решения

По крайней мере на GCC с -O4 кажется функция

inline void lock( int & waitCounter){
while( barrier
.test_and_set( std::memory_order_acquire ) )
waitCounter+=5;
waitCounter+=2;

сводится к коду, который ведет подсчет инструкций, которые сам использует

.L5:
add DWORD PTR [rdi], 5
.L3:
mov eax, edx
xchg    al, BYTE PTR barrier[rip]

test    al, al
jne .L5
add DWORD PTR [rdi], 2
ret

Это далеко не полный ответ, но может дать идею.

0

По вопросам рекламы [email protected]