Какая минимальная ассемблера x86, необходимых для взаимоблокировки


Для реализации спин-замка в сборке. Здесь я публикую решение, которое я придумал. Это правильно? Вы знаете более короткий вариант?

Замок:

    mov ecx, 0
.loop:
    xchg [eax], ecx
    cmp ecx, 0
    je .loop

Релиз:

    lock dec dword [eax]

Eax инициализируется до -1 (что означает, что блокировка свободна). Это должно работать для многих потоков (не обязательно 2).

3 2

3 ответа:

Самым коротким, вероятно, будет:

acquire:
    lock bts [eax],0
    jc acquire

release:
    mov [eax],0

Для производительности лучше всего использовать подход" test, test and set " и использовать pause, например:

acquire:
    lock bts [eax],0    ;Optimistic first attempt
    jnc l2              ;Success if acquired
l1:
    pause
    test [eax],1        
    jne l1              ;Don't attempt again unless there's a chance

    lock bts [eax],0    ;Attempt to acquire
    jc l1               ;Wait again if failed

l2:

release:
    mov [eax],0

Для отладки можно добавить дополнительные данные, чтобы облегчить обнаружение проблем, например:

acquire:
    lock bts [eax],31         ;Optimistic first attempt
    jnc l2                    ;Success if acquired

    mov ebx,[CPUnumber]
    lea ebx,[ebx+0x80000000]
    cmp [eax],ebx             ;Is the lock acquired by this CPU?
    je .bad                   ; yes, deadlock
    lock inc dword [eax+4]    ;Increase "lock contention counter"
l1:
    pause
    test [eax],0x80000000        
    jne l1                    ;Don't attempt again unless there's a chance

    lock bts [eax],31         ;Attempt to acquire
    jc l1                     ;Wait again if failed

l2: mov [eax],ebx             ;Store CPU number

release:
    mov ebx,[CPUnumber]
    lea ebx,[ebx+0x80000000]
    cmp [eax],ebx             ;Is lock acquired, and is CPU same?
    jne .bad                  ; no, either not acquired or wrong CPU
    mov [eax],0

Ваш код прекрасен, но если вы ищете высокую производительность, я бы предложил вместо этого:

  xor ecx, ecx
.loop:
  lock xchg [eax], ecx
  test ecx, ecx
  jz .loop

Причины:

  • xor ecx, ecx меньше и не требует литерала,и современные процессоры имеют этот жесткий провод для быстрого регистра нуля.
  • test ecx, ecx может быть немного меньше и быстрее, чем cmp ecx, 0, потому что вам не нужно загружать литерал и test - это просто побитовая операция, а не вычитание.

P.S. Я всегда ставлю туда префикс замка независимо от того, подразумевается ли это, по причинам читаемости - это делает очевидным, что я делаю заблокированную операцию.

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

Другие ответы упоминают производительность, и это показывает базовое незнание того, как работают замки.

Когда инициируется попытка блокировки, ядро, о котором идет речь, вызывает сигнал на одном из своих выводов (LOCK), который сообщает всем другим ядрам, их кэшам, всей памяти и всем устройствам управления шинами (поскольку они могут обновлять ОЗУ независимо от ядер), чтобы завершить любые выдающиеся операции памяти. Когда сделав это, они коллективно вызывают другой сигнал-lock Reconnaissance (LOCKA) - который возвращается в исходное ядро и происходит обмен памятью. После этого сигнал блокировки выключается.

Как только вы прибыли сюда, вы можете посмотреть на значение, которое вы получили с помощью xchg. Если выяснится, что другая задача/поток владеет блокировкой, вам нужно будет снова выполнить последовательность блокировки.

Предположим, что самым медленным устройством для управления шиной на вашем компьютере является карта PCI 33 МГц. Если он что-то делает, ему потребуется любое количество тактов шины PCI для завершения. Каждый цикл там означает сто циклов ожидания на процессоре с частотой 3,3 ГГц. Поставьте это в перспективе сохранения цикла или двух в последовательности блокировки. В процессоре, чипсете и материнской плате есть несколько шин, и некоторые из них, все или ни одна из них не могут быть активны в любой момент времени - например, при инициировании блокировки. Активная шина, которая дольше всего отвечает с помощью LOCKA, определяет скорость блокировки завершает.

Попробуйте сами: измерьте, сколько времени требуется, чтобы сделать десять миллионов спин-локов (захватить и отпустить).

Я написал еще несколько о spinlocks здесь, здесь и здесь.

Трюк производительности с шинными блокировками (spinlocks, critical sections in Windows) заключается в том, чтобы использовать их как можно реже, что означает организацию ваших данных, чтобы сделать это возможным. Блокировка шины, скорее всего, завершится быстрее на несерверном компьютере. Это потому, что автобус-мастеринг устройства на сервере работают более или менее постоянно. Таким образом, если ваше приложение основано на сервере, экономия на блокировках шины может иметь решающее значение для поддержания производительности.

EDIT

Питеру Кордесу,

Вы утверждаете, что

... это не связано с освоением шины, по крайней мере, не на процессорах, так как по крайней мере Нехалем

Из последнего руководства по программированию intel System:

8.1.4 влияние операции блокировки на внутренний процессор Кэши

Для процессоров Intel486 и Pentium сигнал блокировки# всегда утверждается на шине во время работы блокировки, даже если площадь заблокированная память кэшируется в процессоре.

Для семейств процессоров P6 и более поздних, если область памяти блокирование во время операции блокировки кэшируется в процессоре, который выполняет операцию блокировки как память обратной записи и является полностью содержится в строке кэша, процессор не может утверждать, что Блокировка# сигнал на автобусе. Вместо этого он изменит расположение памяти внутренне и позволить механизму когерентности кэша гарантировать, что операция проводится атомарно. Эта операция называется " кэширование запирающий."Механизм когерентности кэша автоматически предотвращает два или больше процессоров, которые кэшировали ту же область памяти из одновременно изменяя данные в этой области.

Во втором абзаце говорится

... процессор не может утверждать сигнал блокировки# на автобусе.

Я не знаю, как вы, но для меня, по крайней мере, "не может" звучит подозрительно непохоже на "не будет" или "не будет". Числа, которые я привел, могут быть правильными или нет - даже я иногда ошибаюсь, - но я призываю вас прекратить цитировать тот или иной" авторитет " и вместо этого пачкать руки, делая работу, чтобы либо опровергнуть мои числа, найти ошибку(ы) или несоответствия. Я включил соответствующий исходный код в другой поток (где я также ворчал по поводу ваших возвышенных, теоретических, основанных на кресле комментариев), так что вам не потребуется вечность, чтобы начать.

Начните, например, с доказательства того, что я

Завышение стоимости блокировки в случае отсутствия конкуренции ...

Я с нетерпением жду вашего анализа.