Сравните и поменяйте местами машинный код на C

Как бы вы написали функцию на C, которая выполняет атомарное сравнение и замену целочисленного значения, используя встроенный машинный код (предположим, скажем, архитектуру x86)? Может ли он быть более конкретным, если он написан только для процессора i7?

Работает ли перевод как ограничитель памяти, или он просто обеспечивает порядок упорядочения только в той области памяти, которая включена в сравнение и обмен? Насколько это дорого по сравнению с забором памяти?

Спасибо.

5 ответов

Решение

Самый простой способ сделать это, вероятно, с помощью встроенного компилятора, такого как _InterlockedCompareExchange (). Это похоже на функцию, но на самом деле это особый случай в компиляторе, который сводится к одной машинной операции. В случае с MSVC x86, это также работает как ограничение чтения / записи, но это не обязательно верно для других платформ. (Например, на PowerPC вам нужно явно запустить lwsync для предотвращения переупорядочения памяти.)

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

Вы можете использовать CMPXCHG инструкция с LOCK префикс для атомарного исполнения.

Например

lock cmpxchg DWORD PTR [ebx], edx

или же

lock cmpxchgl %edx, (%ebx)

Это сравнивает значение в регистре EAX со значением по адресу, сохраненному в регистре EBX, и сохраняет значение в регистре EDX в этом месте, если они совпадают, в противном случае оно загружает значение по адресу, сохраненному в регистре EBX, в EAX.

Вам необходимо иметь 486 или более позднюю версию, чтобы эта инструкция была доступна.

Если ваше целочисленное значение 64-битное, используйте cmpxchg8b 8-байтовое сравнение и обмен в IA32 x86. Переменная должна быть выровнена на 8 байт.

Example:
      mov   eax, OldDataA           //load Old first 32 bits
      mov   edx, OldDataB           //load Old second 32 bits
      mov   ebx, NewDataA           //load first 32 bits
      mov   ecx, NewDataB           //load second 32 bits
      mov   edi, Destination        //load destination pointer
      lock cmpxchg8b qword ptr [edi]
      setz  al                      //if transfer is succesful the al is 1 else 0

Если префикс LOCK пропущен в инструкциях атомарного процессора, атомарная работа в многопроцессорной среде не гарантируется.

В многопроцессорной среде сигнал LOCK# обеспечивает исключительное использование процессором любой совместно используемой памяти, пока сигнал утверждается. Справочник по инструкциям Intel

Без префикса LOCK операция не будет прервана никаким событием (прерыванием) только на текущем процессоре / ядре.

Интересно отметить, что некоторые процессоры не обеспечивают сравнение-обмен, а вместо этого предоставляют некоторые другие инструкции ("Load Linked" и "Conditional Store"), которые можно использовать для синтеза, к сожалению, названного Compare-and-swap (имя звучит так, как будто оно должно быть похоже на "сравнение-обмен", но на самом деле его следует называть "сравнение-сохранение", так как оно выполняет сравнение, сохраняет, если значение совпадает, и указывает, соответствует ли значение и выполнено ли сохранение). Инструкции не могут синтезировать семантику сравнения-обмена (которая предоставляет значение, прочитанное в случае сбоя сравнения), но в некоторых случаях могут избежать проблемы ABA, которая присутствует в Compare-Exchange. Многие алгоритмы описаны в терминах операций "CAS", потому что они могут использоваться в обоих стилях ЦП.

Инструкция "Load Linked" указывает процессору прочитать ячейку памяти и посмотреть каким-либо образом, не может ли она быть записана. Инструкция "Условное хранилище" инструктирует процессор записывать ячейку памяти только в том случае, если ничего не могло быть записано с момента последней операции "Load Linked". Обратите внимание, что определение может быть пессимистичным; например, обработка прерывания может сделать недействительной последовательность "Load-Linked"/"Conditional Store". Аналогично, в многопроцессорной системе последовательность LL/CS может быть признана недействительной другим ЦП, осуществляющим доступ к местоположению в той же строке кэша, что и отслеживаемое местоположение, даже если фактическое отслеживаемое местоположение не было затронуто. При обычном использовании LL/CS используются очень близко друг к другу, с повторным циклом, так что ошибочные аннулирования могут немного замедлить ход событий, но не вызовут особых проблем.

Другие вопросы по тегам