Я пишу какой-то незакрепленный код, и у меня появился интересный образец, но я не уверен, будет ли он вести себя так, как ожидалось, при упорядоченном упорядочении памяти.
Самый простой способ объяснить это - пример:
std::atomic<int> a, b, c;
auto a_local = a.load(std::memory_order_relaxed);
auto b_local = b.load(std::memory_order_relaxed);
if (a_local < b_local) {
auto c_local = c.fetch_add(1, std::memory_order_relaxed);
}
Обратите внимание, что во всех операциях используется std::memory_order_relaxed
.
Очевидно, что в потоке, который выполняется, нагрузки для a
и b
должны выполняться до того, как будет оценено условие if
.
Аналогично, операция чтения-изменения-записи (RMW) в c
должна выполняться после того, как условие будет оценено (поскольку оно обусловлено этим условием...).
Что я хочу знать, этот код гарантирует, что значение c_local
не менее актуально, чем значения a_local
и b_local
? Если да, то как это возможно, учитывая расслабленный порядок памяти? Является ли зависимость управления вместе с операцией RWM действующей как своего рода забор? (Обратите внимание, что там даже не существует соответствующего релиза.)
Если это верно, я считаю, что этот пример также должен работать (при условии, что он не переполняется) - я прав?
std::atomic<int> a(0), b(0);
// Thread 1
while (true) {
auto a_local = a.fetch_add(1, std::memory_order_relaxed);
if (a_local >= 0) { // Always true at runtime
b.fetch_add(1, std::memory_order_relaxed);
}
}
// Thread 2
auto b_local = b.load(std::memory_order_relaxed);
if (b_local < 777) {
// Note that fetch_add returns the pre-incrementation value
auto a_local = a.fetch_add(1, std::memory_order_relaxed);
assert(b_local <= a_local); // Is this guaranteed?
}
В потоке 1 есть зависимость управления, которая, как я подозреваю, гарантирует, что a
всегда увеличивается до того, как b
будет увеличиваться (но каждый из них будет увеличивать шейку и шею). В потоке 2 существует другая управляющая зависимость, которая, как я подозреваю, гарантирует, что b
загружается в b_local
до того, как a
будет увеличено. Я также думаю, что значение, возвращаемое из fetch_add
, будет, по крайней мере, столь же новым, как любое наблюдаемое значение в b_local
, и поэтому assert
должно сохраняться. Но я не уверен, так как это значительно отличается от обычных примеров упорядочения памяти, и мое понимание модели памяти С++ 11 не является совершенным (у меня есть проблемы с рассуждением об этих эффектах упорядочения памяти с какой-либо степенью определенности). Любые идеи будут оценены!
Обновление. Как было сказано в комментариях к bames53, учитывая достаточно умный компилятор, возможно, что if
может быть полностью оптимизирован при правильных обстоятельствах, и в этом случае расслабленная нагрузки могут быть переупорядочены после RMW, в результате чего их значения будут более актуальными, чем возвращаемое значение fetch_add
(assert
может срабатывать в моем втором примере). Однако, что, если вместо if
вставлен a atomic_signal_fence
(not atomic_thread_fence
)? Это, конечно, не может быть проигнорировано компилятором независимо от того, какие оптимизации сделаны, но гарантирует ли он, что код ведет себя так, как ожидалось? Разрешено ли в этом случае процессору делать переупорядочивание?
Затем второй пример выглядит следующим образом:
std::atomic<int> a(0), b(0);
// Thread 1
while (true) {
auto a_local = a.fetch_add(1, std::memory_order_relaxed);
std::atomic_signal_fence(std::memory_order_acq_rel);
b.fetch_add(1, std::memory_order_relaxed);
}
// Thread 2
auto b_local = b.load(std::memory_order_relaxed);
std::atomic_signal_fence(std::memory_order_acq_rel);
// Note that fetch_add returns the pre-incrementation value
auto a_local = a.fetch_add(1, std::memory_order_relaxed);
assert(b_local <= a_local); // Is this guaranteed?
Еще одно обновление. После прочтения всех ответов до сих пор и расчёта по стандарту самостоятельно, я не думаю, что можно показать, что код правильный, используя только стандарт. Итак, может ли кто-нибудь придумать встречный пример теоретической системы, которая соответствует стандарту, а также срабатывает утверждение?