_Compiler_barrier() 在 32 位读取上的用途
Purpose of _Compiler_barrier() on 32bit read
我一直在逐步完成在 VS2017 上使用 64 位项目分配给 atomic_long
类型时涉及的函数调用。我特别想看看当我将 atomic_long
复制到 none 原子变量时会发生什么,以及它周围是否有任何锁定。
atomic_long ll = 10;
long t2 = ll;
最终它以这个调用结束(我删除了一些 ifdef
ed 出来的代码)
inline _Uint4_t _Load_seq_cst_4(volatile _Uint4_t *_Tgt)
{ /* load from *_Tgt atomically with
sequentially consistent memory order */
_Uint4_t _Value;
_Value = *_Tgt;
_Compiler_barrier();
return (_Value);
}
现在,我读到 from MSDN 32 位值的普通读取将是原子的:
Simple reads and writes to properly-aligned 32-bit variables are
atomic operations.
...这解释了为什么没有 Interlocked
功能只用于阅读;只有 changing/comparing 的那些。我想知道 _Compiler_barrier()
位在做什么。这是 #define
d as
__MACHINE(void _ReadWriteBarrier(void))
...我在 MSDN 上再次发现这个
Limits the compiler optimizations that can reorder memory accesses
across the point of the call.
但我不明白,因为除了 return
调用之外没有其他内存访问;编译器肯定不会将赋值移动到下面,对吗?
有人可以澄清这个障碍的目的吗?
_Load_seq_cst_4
是一个 inline
函数 。编译器屏障用于阻止内联到的调用函数中的后续代码重新排序。
例如,考虑阅读 SeqLock. (Over-simplified from this actual implementation).
#include <atomic>
atomic<unsigned> sequence;
atomic_long value;
long seqlock_try_read() {
// this would normally be the body of a retry-loop;
unsigned seq1 = sequence;
long tmpval = value;
unsigned seq2 = sequence;
if (seq1 == seq2 && (seq1 & 1 == 0)
return tmpval;
else
// writer was modifying it, we should retry the loop
}
如果我们不阻止编译时重新排序,编译器可以将 sequence
的两次读取合并为一次访问,可能像这样
long tmpval = value;
unsigned seq1 = sequence;
unsigned seq2 = sequence;
这会破坏锁定机制(编写器在修改数据之前递增 sequence
一次,然后在完成后再次递增)。读者是完全无锁的,但这不是 "lock-free" 算法,因为如果作者在更新过程中卡住,读者将无法阅读任何内容。
障碍 within 每个 load
函数阻止内联后与其他事物重新排序。
(C++11 内存模型很弱,但 x86 内存模型很强,只允许 StoreLoad 重新排序。用后面的 loads/stores 阻止编译时重新排序足以给你一个 acquire /运行时顺序一致性加载。)
顺便说一句,一个更好的例子可能是某些非 atomic
变量在看到 atomic
标志中的特定值后 read/written。 MSVC 可能已经避免了原子访问的重新排序或合并,并且在 seqlock 中,受保护的数据也必须是 atomic
.
我一直在逐步完成在 VS2017 上使用 64 位项目分配给 atomic_long
类型时涉及的函数调用。我特别想看看当我将 atomic_long
复制到 none 原子变量时会发生什么,以及它周围是否有任何锁定。
atomic_long ll = 10;
long t2 = ll;
最终它以这个调用结束(我删除了一些 ifdef
ed 出来的代码)
inline _Uint4_t _Load_seq_cst_4(volatile _Uint4_t *_Tgt)
{ /* load from *_Tgt atomically with
sequentially consistent memory order */
_Uint4_t _Value;
_Value = *_Tgt;
_Compiler_barrier();
return (_Value);
}
现在,我读到 from MSDN 32 位值的普通读取将是原子的:
Simple reads and writes to properly-aligned 32-bit variables are atomic operations.
...这解释了为什么没有 Interlocked
功能只用于阅读;只有 changing/comparing 的那些。我想知道 _Compiler_barrier()
位在做什么。这是 #define
d as
__MACHINE(void _ReadWriteBarrier(void))
...我在 MSDN 上再次发现这个
Limits the compiler optimizations that can reorder memory accesses across the point of the call.
但我不明白,因为除了 return
调用之外没有其他内存访问;编译器肯定不会将赋值移动到下面,对吗?
有人可以澄清这个障碍的目的吗?
_Load_seq_cst_4
是一个 inline
函数 。编译器屏障用于阻止内联到的调用函数中的后续代码重新排序。
例如,考虑阅读 SeqLock. (Over-simplified from this actual implementation).
#include <atomic>
atomic<unsigned> sequence;
atomic_long value;
long seqlock_try_read() {
// this would normally be the body of a retry-loop;
unsigned seq1 = sequence;
long tmpval = value;
unsigned seq2 = sequence;
if (seq1 == seq2 && (seq1 & 1 == 0)
return tmpval;
else
// writer was modifying it, we should retry the loop
}
如果我们不阻止编译时重新排序,编译器可以将 sequence
的两次读取合并为一次访问,可能像这样
long tmpval = value;
unsigned seq1 = sequence;
unsigned seq2 = sequence;
这会破坏锁定机制(编写器在修改数据之前递增 sequence
一次,然后在完成后再次递增)。读者是完全无锁的,但这不是 "lock-free" 算法,因为如果作者在更新过程中卡住,读者将无法阅读任何内容。
障碍 within 每个 load
函数阻止内联后与其他事物重新排序。
(C++11 内存模型很弱,但 x86 内存模型很强,只允许 StoreLoad 重新排序。用后面的 loads/stores 阻止编译时重新排序足以给你一个 acquire /运行时顺序一致性加载。
顺便说一句,一个更好的例子可能是某些非 atomic
变量在看到 atomic
标志中的特定值后 read/written。 MSVC 可能已经避免了原子访问的重新排序或合并,并且在 seqlock 中,受保护的数据也必须是 atomic
.