c++11中的内存建模测试,对memory_order_relaxed很好奇
memory modeling test in c++11 , curious for memory_order_relaxed
我已经阅读了网页:
http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/
然后对g++4.8.1编译的测试源进行编码,cpu为英特尔。。。
global var : r1=0;r2=0;x=0;y=0;
Thread1 :
x = 1 ; //line 1
r1 = y ; //line 2
Thread2 :
y = 1 ; //line 3
r2 = x ; //line 4
并且我将得到r1==0&;r2==0,有时同时运行线程1和线程2,我知道这是之前执行的y(第2行)和x(第4行)的负载存储x(第1行),存储y(第3行)。。。。即使是像intel cpu这样的强内存模型,存储之前的负载紊乱仍然发生,这就是为什么r1==0&;r2==0仍然发生在这次测试中!!!!
参考c++11内存模型,我更改了如下来源:
global vars :
int r1=0,r2=0 ;
atomic<int> x{0} ;
atomic<int> y{0} ;
Thread1 :
x.store(1,memory_order_acq_rel) ;
r1=y.load(memory_order_relaxed) ;
Thread2 :
y.store(1,memory_order_acq_rel) ;
r2=x.load(memory_order_relaxed) ;
这一次,没有r1==0&;r2==0发生时,我使用的内存顺序到我一开始提到的网站,请参阅声明:
memory_order_aquire:确保后续加载不会在当前加载或任何先前加载之前移动。
memory_order_release:前面的存储不会移过当前存储或任何后续存储。
memory_order_aq_rel:结合前面两个保证
memory_order_relaxed:所有重新排序都可以。
看看工作。。。我仍然做另一个测试,我把代码改为:
global vars :
int r1=0,r2=0 ;
atomic<int> x{0} ;
atomic<int> y{0} ;
Thread1 :
x.store(1,memory_order_relaxed) ;
r1=y.load(memory_order_relaxed) ;
Thread2 :
y.store(1,memory_order_relaxed) ;
r2=x.load(memory_order_relaxed) ;
让我困惑的是,这个测试仍然没有得到r1==0&;r2==0!!如果这种情况有效,为什么要使用memoryorderaq_rel呢?或者这只起作用在英特尔cpu?其他类型的cpu仍然需要x和y的存储中的memoryorderaq_rel?
你的第一个实验的结果很有趣:"当同时运行thread1和thread2时,我有时会得到r1==0&r2==0……即使是像intel cpu这样的强内存模型,在存储之前的负载也会紊乱",但这不仅仅是因为你认为的原因。Atomics不仅阻止处理器和缓存子系统重新排序内存访问,还阻止编译器。Coliru的GCC 4.8优化了此代码,以便在存储之前使用加载指令进行组装:
_Z7thread1v:
.LFB326:
.cfi_startproc
movl y(%rip), %eax
movl $1, x(%rip)
movl %eax, r1(%rip)
ret
即使处理器保证了这里的内存顺序,你也需要一些围栏来防止编译器把事情搞砸。
由于使用memory_order_acq_rel
作为store
的内存排序,您的第二个程序格式不正确。acquire
仅对加载有意义,release
仅对存储有意义,因此memory_order_acq_rel
仅作为原子读-修改-写操作(如exchange
或fetch_add
)的排序有效。用memory_order_release
替换m_o_a_r
实现了您想要的语义,并且生成的程序集再次令人感兴趣:
_Z7thread1v:
.LFB332:
.cfi_startproc
movl $1, x(%rip)
movl y(%rip), %eax
movl %eax, r1(%rip)
ret
这些指令正是我们期望生成的,没有特殊的围栏指令。处理器内存模型足够强大,可以为普通的旧mov
指令提供必要的排序保证。在这种情况下,原子只需要告诉编译器不要插手代码。
尽管生成了与第二个程序相同的程序集,但第三个程序(技术上)是不可预测的:
_Z7thread1v:
.LFB332:
.cfi_startproc
movl $1, x(%rip)
movl y(%rip), %eax
movl %eax, r1(%rip)
ret
虽然这次的结果是一样的,但不能保证编译器不会像第一个程序那样选择重新排序指令。当您升级编译器、引入其他指令或出于任何其他原因时,结果可能会发生变化。如果你开始在ARM上编译,所有的赌注都会被取消;)同样有趣的是,尽管放宽了源程序中的要求,但生成的汇编程序是相同的。在处理器体系结构的限制之外,没有办法放松内存排序。
这里有很多问题:(1) 发布和获取必须成对。否则,它们不会建立同步,也不会保证任何东西。(2) 即使您在示例中释放存储并获取负载,内存模型仍然允许r1=r2=0。您需要将所有内容设置为seq_cst以禁止执行。(3) 我们在http://demsky.eecs.uci.edu/c11modelchecker.html用于测试C11原子代码。它将为您提供在合理解释C/C++11内存模型的情况下允许的所有执行。
您可能还没有在当前GCC版本上看到这些有趣的行为,因为至少早期版本忽略了内存排序参数,并始终使用seq_cst。如果GCC改变了这一点,您可以看到r1=r2=0。
- Python ctype 'c_char_p' Memory Leak
- Ascending order c++
- 模式"allocate memory or use existing data"
- 如何处理Boost Spirit X3导致Visual Studio 2019 "static initialization order fiasco"?
- Win32 API - HWND "{unused = ???} Unable to read memory"错误
- "in-situ without memory allocation" 字符串的愚蠢实现意味着什么?
- C++ "Using Uninitialized Memory.. (variable name) "
- "Memory Fragmentation"这仍然是一个问题?
- 封送指向结构异常"cannot Read or Write protected memory"的指针数组的指针数组
- 向量数组"Cannot access memory at address"
- 什么是"Reading unbounded stream from standard input (Memory Management)"的例子
- 我在 C++ "out_of_range at memory location"有问题
- QML QQmlPropertyList - 包含的对象生存期和'memory rules'
- Java 本机访问代码错误:"Invalid memory access"
- 编写"anti-lack of memory"异常安全代码
- 什么__asm挥发性("pause" ::: "memory");男孩
- "static initialization order fiasco"是 constexpr 变量的问题吗?
- 在使用 In Order 遍历成员函数时引发异常(堆栈溢出)时出现问题
- 将 Dr. Memory 与 Visual Studio 项目配合使用时出错:缺少应用程序所需的库
- 如何修复代码中的"Invalid memory reference"错误?