Одним из способов реализации нагрузки загрузки в asm на некоторых ISA является обычная нагрузка , за которой следует забор, например, на ISA, таких как PowerP C или ARM, до того как ARMv8 представил ldar
/ ldaxr
, Этот более поздний забор может быть пропущен, если порядок сбоя не включает в себя приобретение.
LL / S C CAS_weak может выглядеть примерно так в псевдоасмере для реального ISA:
ll r0, mem
cmp r0, r1
jne .fail # early-out compare fail
sc r2, mem # let's pretend this sets CF condition code on SC failure
jc .fail # jump if SC failed
lwsync # LoadLoad, StoreStore, and LoadStore but not StoreLoad
... CAS success path
.fail: # we jump here without having executed any barriers
... CAS failure path
Это (я думаю) может быть допустимой реализацией mem.compare_exchange_weak(r1, r2, mo_acquire, mo_relaxed);
на некоторых типах машин.
Это только операция получения, поэтому весь RMW может переупорядочиваться с более ранними операциями ( природа LL / S C удерживает их в глобальном порядке). Перед операцией нет барьера, только после.
lwsync
- это инструкция барьера PowerP C, которая блокирует все переупорядочения, кроме StoreLoad (т. Е. Не хранит буфер * flu sh). https://preshing.com/20120913/acquire-and-release-semantics/ и https://preshing.com/20120930/weak-vs-strong-memory-models/
Чтобы реализовать CAS(..., acq_rel, relaxed)
на большинстве ISA, мы также установили бы барьер до LL / S C (чтобы отделить его от более ранних операций, создав выпускную часть). Это будет выполняться даже на пути сбоя, но не создаст семантику получения. Он не будет отделять нагрузку от более поздних операций.
AFAIK, вы не захотите ставить забор между LL и S C, чтобы, возможно, пропустить, если сравнение не удалось. Это бы удлинило транзакцию и дало бы ей больше шансов потерпеть неудачу из-за активности других потоков.
Как всегда при реализации модели памяти C ++ поверх реального asm, вы делаете что-то настолько сильное, насколько это необходимо, но не более сильное , учитывая ограничения того, что обеспечивает базовый ISA. Большинство ISA не позволяют сделать так, чтобы путь сбоя CAS (acq_rel, relaxed) был фактически таким же дешевым, как простая расслабленная нагрузка , либо потому, что это невозможно, либо потому, что это повредило бы производительности обычного случая. Но в некоторых случаях он все еще может быть на меньше дороже, чем если бы сторона отказа должна была получить семантику.
Некоторые ISA (например, ARM), очевидно, имеют только полные барьеры (dsb ish
), поэтому даже acq_rel заканчивает тем, что истощал буфер хранилища. (Таким образом, ARMv8, представляющий хранилища с загрузкой и последовательным выпуском, был очень приятным, поскольку он идеально соответствует семантике C ++ seq-cst и потенциально может быть намного дешевле барьера.)