gpt4 book ai didi

c++ - C11 独立内存屏障 LoadLoad StoreStore LoadStore StoreLoad

转载 作者:行者123 更新时间:2023-12-02 13:45:28 25 4
gpt4 key购买 nike

我想在原子和非原子操作之间使用独立内存屏障(我认为无论如何这根本不重要)。我想我理解存储屏障和加载屏障的含义以及 4 种可能的内存重新排序; LoadLoad , StoreStore , LoadStore , StoreLoad

但是,我总是发现获取/释放概念令人困惑。因为在阅读文档时,acquire不仅讲加载,还讲存储,而release不仅讲存储,还讲加载。另一方面,普通负载屏障仅为您提供负载保证,而普通存储屏障仅为您提供存储保证。

我的问题如下。在 C11/C++11 中考虑独立是否安全 atomic_thread_fence(memory_order_acquire)作为负载屏障(防止 LoadLoad 重新排序)和 atomic_thread_fence(memory_order_release)作为商店屏障(防止 StoreStore 重新订购)?

如果以上是正确的,我可以用什么来防止 LoadStoreStoreLoad重新排序?

当然,我对可移植性感兴趣,我不关心上述内容在特定平台上产生什么结果。

最佳答案

不,在宽松加载之后的获取屏障可以变成获取加载(与仅使用获取加载相比,在某些 ISA 上效率低下),因此它也必须阻止 LoadStore作为LoadLoad

参见https://preshing.com/20120913/acquire-and-release-semantics/一些非常有用的排序图显示了这一点,并且发布存储需要确保所有先前的加载和存储都是“可见的”,因此需要阻止 StoreStore 和 LoadStore。 (商店部分为第二的重新排序)。特别是这张图:

enter image description here

还有https://preshing.com/20130922/acquire-and-release-fences/

https://preshing.com/20131125/acquire-and-release-fences-dont-work-the-way-youd-expect/解释了 acq 和 rel 栅栏的 2 路性质与 acq 或 rel 操作(如加载或存储)的 1 路性质。显然有些人对 atomic_thread_fence() 的保证有误解,认为它太弱了。

为了完整起见,请记住编译器必须针对 compile-time reordering 强制执行这些排序规则。 ,不仅仅是运行时。

考虑作用​​于 C++ 抽象机中的 C++ 加载/存储的屏障可能最有效,无论它在 asm 中如何实现。但也有像 PowerPC 这样的极端情况,其中的心理模型并不能涵盖所有内容(IRIW 重新排序,见下文)。

我确实建议尝试从获取和释放操作的角度进行思考,以确保其他操作彼此的可见性,并且绝对不要编写仅使用宽松操作和单独屏障的代码。这可以安全但效率往往较低。

<小时/>

关于 ISO C/C++ 内存/线程间排序的所有内容都是根据获取加载从释放存储中查看值来正式定义的,从而创建“同步”关系,而不是关于控制本地重新排序的栅栏。

std::atomic 确实明确保证所有线程同时看到变化的一致共享内存状态的存在。在您使用的心理模型中,在读取/写入单个共享状态时进行本地重新排序,当一个线程使其存储对某些其他线程可见之前,IRIW 重新排序可能会发生。所有其他线程。 (就像可以 happen in practice on some SMT PowerPC CPUs. )。

实践all C/C++ implementations run threads across cores that do have a cache-coherent view of shared memory因此,在读/写连贯共享内存方面的心理模型具有控制本地重新排序的障碍。但请记住,C++ 文档不会讨论重新排序,而只是讨论是否首先保证任何顺序。

<小时/>

要深入了解 C++ 如何描述内存模型与实际架构的 asm 内存模型如何描述之间的区别,另请参阅 How to achieve a StoreLoad barrier in C++11? (包括我在那里的回答)。另外Does atomic_thread_fence(memory_order_seq_cst) have the semantics of a full memory barrier?是相关的。

fence(seq_cst) 包括 StoreLoad(如果该概念甚至适用于给定的 C++ 实现)。我认为根据局部障碍进行推理,然后将其转换为 C++ 基本上是可行的,但请记住,它并没有模拟 C++ 允许的 IRIW 重新排序的可能性,而这在现实生活中的某些 POWER 上会发生硬件。

另请记住,var.load(acquire) 可能比 var.load(relaxed) 更高效; fence(acquire); 在某些 ISA 上,尤其是 ARMv8。

例如this example on Godbolt ,由 GCC8.2 为 ARMv8 编译 -O2 -mcpu=cortex-a53

#include <atomic>
int bad_acquire_load(std::atomic<int> &var){
int ret = var.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
return ret;
}

bad_acquire_load(std::atomic<int>&):
ldr r0, [r0] // plain load
dmb ish // FULL BARRIER
bx lr
int normal_acquire_load(std::atomic<int> &var){
int ret = var.load(std::memory_order_acquire);
return ret;
}

normal_acquire_load(std::atomic<int>&):
lda r0, [r0] // acquire load
bx lr

关于c++ - C11 独立内存屏障 LoadLoad StoreStore LoadStore StoreLoad,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61710818/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com