Я просматриваю книгу Мары «Атомика и блокировки», в которой показан пример кода, в котором предполагается, что функции a и b выполняются одновременно:
static X: AtomicI32 = AtomicI32::new(0);
fn a() {
X.fetch_add(5, Relaxed);
X.fetch_add(10, Relaxed);
}
fn b() {
let a = X.load(Relaxed);
let b = X.load(Relaxed);
let c = X.load(Relaxed);
let d = X.load(Relaxed);
println!("{a} {b} {c} {d}");
}
Ссылка: https://marabos.nl/atomics/memory-ordering.html#relaxed
В нем говорится, что, поскольку модифицирует только один поток X
, возможен только один возможный порядок модификации X: 0 -> 5 -> 15.
Мой вопрос: почему это не может быть так: 0 -> 10 -> 15 из-за возможности переупорядочения инструкций? Я спрашиваю об этом, потому что ранее в этой главе было указано:
Логика проверки того, что определенное изменение порядка или другая оптимизация не повлияет на поведение вашей программы, не учитывает другие потоки.
По этой причине я считаю, что компилятор или процессор могут при необходимости оптимизировать и переупорядочить.
@cafce25: В целом это неправда. Два расслабленных атомарных хранилища в разных местах могут стать видимыми для других потоков в порядке, отличном от порядка программы. Ключевым фактором здесь является то, что обе операции выполняются над одним и тем же объектом, а объект является атомарным.
@PeterCordes Это не то, что я сказал, я сказал, что эти утверждения не могут быть переопределены, это отличается от того, что их эффект становится видимым в другом потоке. Хотя, согласитесь, это менее актуально.
@cafce25: Вы сказали, что изменение порядка запрещено в любом случае, когда разница заметна. Это неверно для неатомарных или не-seq_cst порядков памяти для разных объектов, когда наблюдателем является другой поток. Или вы использовали слово «тех» для того, чтобы ограничить его особым случаем в вопросе? Если вы это имели в виду, ваш смысл не совсем понятен.
Есть переупорядочение и есть переупорядочение :)
Атомарные инструкции для отдельных атомарных значений всегда упорядочены, независимо от используемого порядка памяти.
Упорядочение памяти влияет только на порядок операций с памятью над другими значениями (атомарными или нет) относительно атомарной операции.
В формализме C++, используемом в стандарте, порядок модификации переменной совместим с порядком последовательности для модификаций, поступающих из одного и того же потока. Я предполагаю, что Rust работает таким же образом, если его формализм отдаленно похож на C++.
@PeterCordes Да, Rust работает так же, как C++. Можете ли вы разместить здесь соответствующую ссылку на формализм C++? Мне не удалось найти это в той же теме.
@Sibi: согласованность записи и записи (eel.is/c++draft/intro.races#15 ) ссылки происходят до порядка модификации одного объекта. И eel.is/c++draft/intro.races#10 — один из способов, чтобы A произошло раньше B, — это упорядочить его раньше. В терминах C++ «последовательность перед» — это «порядок выполнения программы в одном потоке».
Изменение порядка операторов, при котором можно наблюдать эффект этих операторов, не допускается ни для ЦП, ни для компилятора.