Ответ 1
Ответ здесь не тривиален. То, что происходит и что имеется в виду, зависит от многих вещей. Для базового понимания когерентности/памяти кеша возможно, мои недавние записи в блоге могут быть полезными:
Но в стороне, позвольте мне попытаться ответить на несколько вопросов. Во-первых, нижняя инструкция очень надеется на то, что поддерживается.
compare_swap( C& expected, C desired,
memory_order success, memory_order failure )
Архитектуры не смогут реализовать это точно так, как вы просили. Когда вы указываете memory_order, вы указываете, как может выполняться переупорядочение. Чтобы использовать термины Intel, вы укажете, какой тип забора вы хотите, есть три из них: полный забор, ограждение для груза и ограждение магазина. Просто потому, что вы хотите, чтобы какой-то конкретный забор на этой операции не означал, что он поддерживается, в котором я надеюсь, что он всегда возвращается к полной заборе.
Компилятор, скорее всего, будет использовать инструкцию CMPXCHG
для реализации вызова. Если вы указали что-то другое, кроме расслабленного, это отметит это с помощью lock
, чтобы указать, что функция должна быть атомарной. Независимо от того, является ли это "блокировкой", во многом зависит от того, о чем вы думаете, в терминах "блокировки".
С точки зрения синхронизации памяти вам нужно понять, как работает кеш-когерентность (мой блог может немного помочь). Новые процессоры используют архитектуру ccNUMA (ранее SMP). По сути, "вид" в памяти никогда не выходит из синхронизации. Заграждения, используемые в коде, фактически не приводят к какой-либо промывке. Если два ядра имеют одинаковое расположение памяти, кэшированное в кеш-строке, то они будут отмечены как загрязненные, а другие будут перезагружаться по мере необходимости. Очень простое объяснение очень сложного процесса
Чтобы ответить на ваш последний вопрос, вы всегда должны использовать семантику памяти, которую вы логически должны быть правильными. Большинство архитектур не будут поддерживать все комбинации, которые вы используете в своей программе. Однако во многих случаях вы получите отличную оптимизацию, особенно в тех случаях, когда заказ, который вы запросили, гарантирован без забора (что довольно распространено).
- Ответы на некоторые комментарии:
Вы должны различать, что означает выполнение инструкции записи и запись в ячейку памяти. Это то, что я пытаюсь объяснить в своем сообщении в блоге. К моменту, когда "0" зафиксировано в 0x100, все ядра видят нуль. Написание целых чисел также является атомарным, то есть даже без блокировки, когда вы пишете в местоположение, все ядра сразу будут иметь это значение, если они захотят его использовать.
Проблема в том, что для использования значения, которое вы, скорее всего, загрузили в регистр, любые изменения в местоположении после этого, очевидно, не будут касаться регистра. Вот почему нужны мьютексы, несмотря на когерентную память в кэше.
Что касается противоречивых требований, то в целом вы увидите всевозможные претензии. Являются ли они противоречивыми, происходит именно то, что "видит" "груз" "исполняет" в контексте. Если вы пишете "1" на 0x100, означает ли это, что вы выполнили инструкцию записи или действительно ли CPU зафиксировал это значение. Разница исходит из переупорядочения. Процессор может задерживать запись "1" , но вы можете быть уверены, что в тот момент, когда это произойдет, окончательно зафиксируйте, что "1" все ядра видят его. Забор контролирует это упорядочение.