Может ли CAS выйти из строя для всех потоков? - PullRequest
0 голосов
/ 10 мая 2018

Я читаю о [lock cmpxchg описание]) https://www.felixcloutier.com/x86/CMPXCHG.html):

Эта инструкция может использоваться с префиксом LOCK, чтобы инструкция должна быть выполнена атомарно. Чтобы упростить интерфейс к шине процессора, операнду назначения получает цикл записи без учета результата сравнение. Операнд-адресат записывается обратно, если сравнение не удается; в противном случае исходный операнд записано в пункт назначения. (Процессор никогда производит заблокированное чтение, не производя также заблокированное чтение написать.)

Теперь рассмотрим два потока, выполняющих lock cmpxchg:

Thread 1                Thread 2
mov ebx, 0x4000                mov ebx, 0x4000 ; address
mov edx, 0x62ab6                mov edx, 0x62ab8 ;  new val
mov eax, 0x62ab1                mov eax, 0x62ab1 ;  old
lock cmpxchg [ebx], eax                lock cmpxchg [ebx], eax ;  <----- here

Вопрос в том, может ли блокировка cmpxchg в потоке 1 и 2 завершиться с ошибкой?

С

целевой операнд получает цикл записи без учета результата сравнение

Я мог бы догадаться, что оба потока могут иметь цикл записи, и тогда оба они могут быть возвращены из-за сравнения с устаревшим значением ... Но я не уверен, что это правильно.

Возможно, мне нужно посмотреть детали реализации cas, но она не указана в справочнике по инструкции Intel (по крайней мере, я не смог найти)

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

@ the8472 ответ правильный, но я хотел добавить альтернативный ответ.

https://www.felixcloutier.com/x86/CMPXCHG.html уже определяет поведение достаточно подробно, чтобы исключить возможность ложного сбоя. Если это может произойти сбой по какой-либо причине, кроме значения в памяти, не совпадающего с eax, документы должны будут сказать об этом.

Также можно отметить тот факт, что компиляторы используют один lock cmpxchg для C ++ 11 std::atomic::compare_exchange_strong, из чего можно сделать вывод, что авторы компилятора считают, что lock cmpxchg не может внезапно потерпеть неудачу.

#include <atomic>

bool cas_bool(std::atomic_int *a, int expect, int want) {
    return a->compare_exchange_strong(expect, want);
}

компилируется в (gcc7.3 -O3) :

cas_bool(std::atomic<int>*, int, int):
    mov     eax, esi
    lock cmpxchg    DWORD PTR [rdi], edx
    sete    al
    ret

См. Также Может ли num ++ быть атомарным для 'int num'? для более подробной информации о том, как lock редактируемые инструкции реализованы внутри, и как они взаимодействуют с MESI. (т. е. @ the8472 - это короткая версия: для операнда, который не пересекает строку кэша, ядро ​​просто висит на этой строке кэша, так что ничто другое в системе не может прочитать или записать его в течение lock cmpxchg ** 1027).


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

Пара read + write является атомарной по отношению ко всем другим наблюдателям в системе. Порядок, который вы предлагаете, для read1 / read2 / write1 / abort write2 невозможен, потому что lock cmpxchg является атомарным, поэтому read2 не может появляться между read1 и write1 в глобальном порядке.

Кроме того, этот язык применяется только к шине внешней памяти. Современные процессоры со встроенными контроллерами памяти могут делать все, что угодно (за lock cmpxchg по адресу, разделенному на две строки кэша). Корпорация Intel может публиковать документацию для поставщиков материнских плат для использования при внутреннем тестировании сигналов на шине памяти.

Эта документация может все еще иметь отношение к lock cmpxchg для адреса MMIO, но определенно не для выровненного операнда в памяти обратной записи. В этом случае это просто блокировка кеша. (И это скрытая деталь реализации, независимо от того, записан ли кеш L1d или нет при сбое сравнения). Я думаю, вы могли бы проверить это, посмотрев, не загрязняет ли она строку кэша (то есть переводит ли она в измененное состояние вместо исключительного).

Для получения дополнительной информации о том, как lock cmpxchg может работать внутренне против xchg, см. Ветку чата между мной и @BeeOnRope после моего ответа на Выход из критической области . (В основном, у меня есть идеи, которые могут работать теоретически, но несовместимы с тем, что мы знаем о процессорах Intel x86, и @BeeOnRope указывает на мои ошибки. https://chat.stackoverflow.com/transcript/message/42472667#42472667. Мы очень мало можем точно сказать о мелких деталях эффективность xchg против lock cmpxchg. Конечно, возможно, что xchg удерживает строку кэша на меньшее количество циклов, чем lock cmpxchg, но это необходимо проверить. Я думаю, что xchg имеет большую задержку, если используется обратно. Впрочем, в одном и том же месте из одного потока.)

0 голосов
/ 10 мая 2018

Насколько я понимаю, lock cmpxchg не может внезапно выйти из строя - в отличие от LL / SC - при условии, что значение в адресе памяти действительно совпадает. Он строит эти гарантии из протокола когерентности кэша, принимая исключительное владение строкой кэша и не передавая ее другим ядрам, пока операция не будет завершена.

Таким образом, CAS может потерпеть неудачу только для всех потоков, если какой-либо другой поток записал в ячейку памяти.

...