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

X86 с использованием cmpxchg

Я новичок в использовании сборки gcc inline и задаюсь вопросом, можно ли реализовать на x86 многоядерной машине спин-блокировку (без условий гонки) как (используя синтаксис AT & T):

spin_lock:
mov 0 eax
lock cmpxchg 1 [lock_addr]
jnz spin_lock
ret

spin_unlock:
lock mov 0 [lock_addr]
ret
4b9b3361

Ответ 1

У вас есть правильная идея, но ваш asm сломан:

cmpxchg не может работать с непосредственным операндом, регистрируется только.

lock не является допустимым префиксом для mov. mov к выровненному адресу является атомарным на x86, поэтому вам не нужно lock в любом случае.

Прошло некоторое время с тех пор, как я использовал синтаксис AT & T, надеюсь, что я все вспомнил:

spin_lock:
xorl %ecx, %ecx
incl %ecx
spin_lock_retry:
xorl %eax, %eax
lock; cmpxchgl %ecx, (lock_addr)
jnz spin_lock_retry
ret

spin_unlock:
movl $0 (lock_addr)
ret

Обратите внимание, что GCC имеет атомные встроенные функции, поэтому вам не нужно использовать встроенный asm для выполнения этого:

void spin_lock(int *p)
{
    while(!__sync_bool_compare_and_swap(p, 0, 1));
}

void spin_unlock(int volatile *p)
{
    asm volatile (""); // acts as a memory barrier.
    *p = 0;
}

Как сказано ниже в Боге, заблокированные инструкции несут затраты: каждый, который вы используете, должен очищать ваш кеш и блокировать вашу шину системной памяти, что может быть довольно дорого, если у вас достаточно процессоров. Даже без использования многих процессоров все еще легко и стоит оптимизировать:

void spin_lock(int volatile *p)
{
    while(!__sync_bool_compare_and_swap(p, 0, 1))
    {
        while(*p) _mm_pause();
    }
}

Инструкция pause имеет жизненно важное значение для производительности на процессорах HyperThreading, когда у вас есть код, который вращается таким образом - он позволяет второму потоку выполняться во время вращения первого потока. На CPU, которые не поддерживают pause, он рассматривается как nop.

Ответ 2

Это приведет к меньшему конфликту на шине памяти:

void spin_lock(int *p)
{
    while(!__sync_bool_compare_and_swap(p, 0, 1)) while(*p);
}