通常在互联网上我发现LFENCE在处理器x86中没有任何意义,即它什么都不做,所以相反MFENCE我们可以绝对无痛地使用SFENCE,因为MFENCE= SFENCE+ LFENCE= SFENCE+ NOP= SFENCE.
但是如果LFENCE没有意义,那么为什么我们有四种方法在x86/x86_64中建立顺序一致性:
LOAD(没有围栏)和STORE+MFENCELOAD (没有围栏)和 LOCK XCHGMFENCE+ LOAD和STORE(没有围栏)LOCK XADD(0)和STORE(没有围栏)取自这里:http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
以及Herb Sutter在第34页底部的表演:https://skydrive.live.com/view.aspx?status = 4E86B0CF20EF15AD!24884&app = WordPdf&wdo = 2&authkey =!AMtj_EflYn2507c
如果LFENCE没有做任何事情,那么方法(3)将具有以下含义:SFENCE + LOAD and STORE (without fence)但是SFENCE之前没有任何意义LOAD.即如果LFENCE什么都不做,方法(3)没有意义.
它LFENCE在处理器x86/x86_64中是否有任何意义上的指令?
回答:
1. …
以下是在x86/x86_64中实现顺序一致性的四种方法:
正如它在这里写的:http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
C/C++ 11操作x86实现
- 加载Seq_Cst:MOV(来自内存)
- Store Seq Cst:(LOCK)XCHG //替代方案:MOV(进入内存),MFENCE
注意:有一个C/C++ 11到x86的替代映射,而不是锁定(或屏蔽)Seq Cst存储锁/隔离Seq Cst加载:
- 加载Seq_Cst:LOCK XADD(0)//替代:MFENCE,MOV(来自内存)
- Store Seq Cst:MOV(进入内存)
GCC 4.8.2(x86_64中的GDB)对C++ 11-std :: memory_order_seq_cst使用第一种方法,即LOAD(没有fence)和STORE + MFENCE:
std::atomic<int> a;
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
0x4613e8 <+0x0058> mov 0x38(%rsp),%eax
0x4613ec <+0x005c> mov %eax,0x20(%rsp)
0x4613f0 <+0x0060> mfence
Run Code Online (Sandbox Code Playgroud)
我们知道,MFENCE = LFENCE + SFENCE.然后这段代码我们可以重写为:LOAD(without fence) and STORE+LFENCE+SFENCE
问题:
我阅读了"英特尔架构的英特尔优化指南指南".
但是,我仍然不知道何时应该使用
_mm_sfence()
_mm_lfence()
_mm_mfence()
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释在编写多线程代码时何时应该使用它们?
正如我们从C11-memory_order所知道的那样:http://en.cppreference.com/w/c/atomic/memory_order
从C++ 11-std :: memory_order开始:http://en.cppreference.com/w/cpp/atomic/memory_order
在强排序系统(x86,SPARC,IBM大型机)上, 发布 - 获取顺序是自动的.没有为此同步模式发出额外的CPU指令,只会影响某些编译器优化(例如,禁止编译器在原子存储释放之前移动非原子存储或在原子载荷获取之前执行非原子加载)
但这对于x86-SSE指令是否正确(除了[NT] - 非时间,我们总是必须使用L/S/MFENCE)?
这里说,"sse指令......不要求向后兼容性,并且内存顺序未定义".据信,当需要时,严格的可订购性与旧版本的处理器x86兼容,但是新的命令,即SSE(除了[NT]) - 被剥夺了自动释放 - 获取订单,是吗?
众所周知,在x86上,操作load()和store()内存屏障memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel不需要处理器指令用于缓存和管道,并且汇编程序的代码总是对应std::memory_order_relaxed,并且这些限制仅对编译器的优化是必需的:http:// www. stdthread.co.uk/forum/index.php?topic=72.0
此代码反汇编代码确认store()(MSVS2012 x86_64):
std::atomic<int> a;
a.store(0, std::memory_order_relaxed);
000000013F931A0D mov dword ptr [a],0
a.store(1, std::memory_order_release);
000000013F931A15 mov dword ptr [a],1
Run Code Online (Sandbox Code Playgroud)
但是这段代码并没有为load()(MSVS2012 x86_64)确认这一点,使用lock cmpxchg:
int val = a.load(std::memory_order_acquire);
000000013F931A1D prefetchw [a]
000000013F931A22 mov eax,dword ptr [a]
000000013F931A26 mov edx,eax
000000013F931A28 lock cmpxchg dword ptr [a],edx
000000013F931A2E jne main+36h (013F931A26h)
std::cout << val << "\n";
Run Code Online (Sandbox Code Playgroud)
假设有两个线程分别在x86 CPU0和CPU1上运行.在CPU0上运行的线程执行以下命令:
A=1
B=1
Run Code Online (Sandbox Code Playgroud)
包含A的缓存行,最初由CPU1拥有,并且包含由CPU0拥有的B.
我有两个问题:
如果我理解正确,两个存储都将被放入CPU的存储缓冲区.但是,对于第一个存储A=1,CPU1的高速缓存必须无效,而第二个存储B=1可以立即刷新,因为CPU0拥有包含它的高速缓存行.我知道x86 CPU尊重商店订单.这是否意味着B=1以前不会写入缓存A=1?
假设在CPU1中执行以下命令:
而(B = 0);
打印A.
是否足以加之间只有lfence while和print命令在CPU1不添加之间的SFENCE A=1并B=1在CPU0得到1总是在x86上打印出来?
while (B=0);
lfence
print A
Run Code Online (Sandbox Code Playgroud) 正如我们从之前的回答中所知道的,它是否在处理器x86/x86_64中指示LFENCE?我们不能使用SFENCE而不是MFENCE顺序一致性.
这里的答案表明MFENCE= SFENCE+ LFENCE,即LFENCE没有我们不能提供顺序一致性的东西.
LFENCE 无法重新排序:
SFENCE
LFENCE
MOV reg, [addr]
Run Code Online (Sandbox Code Playgroud)
- 到 - >
MOV reg, [addr]
SFENCE
LFENCE
Run Code Online (Sandbox Code Playgroud)
例如重新排序MOV [addr], reg LFENCE- > LFENCE MOV [addr], reg由机制提供- 存储缓冲区,它重新排序存储 - 负载以提高性能,并且因为LFENCE它不会阻止它.并SFENCE 禁用此机制.
什么机制禁用LFENCE无法重新排序(x86没有机制 - Invalidate-Queue)?
并且只是在理论上或者在现实中重新排序SFENCE MOV reg, [addr]- > MOV reg, [addr] SFENCE可能吗?如果可能,实际上,什么机制,它是如何工作的?
我正在阅读英特尔指令集指南 64-ia-32指南 以了解内存栅栏。我的问题是,以 SFENCE 为例,为了确保所有存储操作都是全局可见的,多核 CPU 是否会停放所有线程甚至在其他内核上运行,直到实现缓存一致性?
x86 multithreading multicore cpu-architecture memory-barriers
我正在检查编译器如何为x86_64上的多核内存屏障发出指令。以下代码是我正在测试的代码gcc_x86_64_8.3。
std::atomic<bool> flag {false};
int any_value {0};
void set()
{
any_value = 10;
flag.store(true, std::memory_order_release);
}
void get()
{
while (!flag.load(std::memory_order_acquire));
assert(any_value == 10);
}
int main()
{
std::thread a {set};
get();
a.join();
}
Run Code Online (Sandbox Code Playgroud)
使用时std::memory_order_seq_cst,我可以看到该MFENCE指令用于任何优化-O1, -O2, -O3。该指令确保刷新了存储缓冲区,因此在L1D缓存中更新了它们的数据(并使用MESI协议确保其他线程可以看到效果)。
但是,当我std::memory_order_release/acquire不进行优化MFENCE使用时,也会使用指令,但是使用-O1, -O2, -O3优化会忽略该指令,并且不会看到其他刷新缓冲区的指令。
在MFENCE不使用的情况下,如何确保将存储缓冲区数据提交给高速缓存以确保内存顺序语义?
以下是使用get / set函数的汇编代码-O3,例如我们在Godbolt编译器资源管理器中获得的代码:
set():
mov DWORD PTR any_value[rip], 10
mov BYTE PTR flag[rip], 1
ret
.LC0:
.string …Run Code Online (Sandbox Code Playgroud) 我知道这不是一个新问题,但在阅读了关于c ++ 11内存栅栏后我感到困惑;
如果我有一个读者线程和一个编写器线程.
我可以使用普通的int吗?
int x = 0; // global
writer reader
x = 1; printf("%d\n", x);
Run Code Online (Sandbox Code Playgroud)
这种行为是不确定的?
我可以在读者线程中获得未定义的值吗?
或者就像使用std::atomic_uint_fast32_t或std::atomic<int>?因此,价值将会到达读者线程 - 最终.
std::atomic<int x = 0; // global
writer reader
x.store(1, std::memory_order_relaxed); printf("%d\n", x.load(std::memory_order_relaxed));
Run Code Online (Sandbox Code Playgroud)
答案取决于我使用的平台吗?(例如x86),所以加载/存储普通int是一条CPU指令?
如果两种行为都相似,那么我是否应该期望两种类型的性能相同?
x86 ×9
c++11 ×5
c++ ×4
assembly ×2
atomic ×2
gcc ×2
x86-64 ×2
c11 ×1
concurrency ×1
cpu ×1
cpu-cache ×1
intrinsics ×1
memory-model ×1
multicore ×1
sse ×1