如果有两个线程访问全局变量,那么许多教程都说使变量volatile变为阻止编译器将变量缓存在寄存器中,从而无法正确更新.但是,访问共享变量的两个线程是通过互斥锁来调用保护的东西不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有那个线程可以访问变量,在这种情况下变量不需要是volatile?
那么多线程程序中volatile的用途/目的是什么?
单个进程中的两个不同线程可以通过读取和/或写入来共享公共存储器位置.
通常,这种(有意)共享是使用lockx86上的前缀使用原子操作实现的,该前缀对于lock前缀本身(即,无竞争成本)具有相当广为人知的成本,并且当实际共享高速缓存行时还具有额外的一致性成本(真或假共享).
在这里,我对生产 - 消费者成本感兴趣,其中单个线程P写入内存位置,另一个线程`C从内存位置读取,都使用普通读取和写入.
在同一个套接字上的不同内核上执行此类操作的延迟和吞吐量是多少,并且在最近的x86内核上在同一物理内核上执行兄弟超线程时进行比较.
在标题中,我使用术语"超级兄弟"来指代在同一核心的两个逻辑线程上运行的两个线程,以及核心间兄弟,以指代在不同物理核心上运行的两个线程的更常见情况.
从关于C++原子类型和操作的C++ 0x提议:
29.1顺序和一致性[atomics.order]
添加一个包含以下段落的新子句.
枚举
memory_order指定详细的常规(非原子)内存同步顺序,如[由N2334或其采用的后继者添加的新部分]中定义的,并且可以提供操作排序.其列举的值及其含义如下.
memory_order_relaxed该操作不会命令内存.
memory_order_release对受影响的内存位置执行释放操作,从而使常规内存写入通过应用它的原子变量对其他线程可见.
memory_order_acquire对受影响的内存位置执行获取操作,从而在通过应用它的原子变量释放的其他线程中进行常规内存写入,对当前线程可见.
memory_order_acq_rel该操作具有获取和释放语义.
memory_order_seq_cst该操作既具有获取和释放语义,另外,具有顺序一致的操作顺序.
提案中较低:
Run Code Online (Sandbox Code Playgroud)bool A::compare_swap( C& expected, C desired, memory_order success, memory_order failure ) volatile可以指定CAS的内存顺序.
我的理解是" memory_order_acq_rel"只需要同步操作所需的那些内存位置,而其他内存位置可能保持不同步(它不会表现为内存栅栏).
现在,我的问题是 - 如果我选择" memory_order_acq_rel"并应用于compare_swap整数类型,例如整数,这通常如何转换为现代消费者处理器(如多核英特尔i7)上的机器代码?那么其他常用的架构(x64,SPARC,ppc,arm)呢?
特别是(假设一个具体的编译器,比如说gcc):
acq_rel在i7 上使用语义是否有任何性能优势?其他架构呢?感谢所有的答案.
英特尔内存模型保证:
http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/
我已经看到声称由于Intel内存模型,SFENCE在x86-64上是多余的,但从来没有LFENCE.上述内存模型规则是否使指令冗余?
我已经了解了不同的缓存映射技术,如直接映射,关联映射和集合关联映射技术,还学习了权衡.但我很好奇现在在intel core i7或AMD处理器中使用了什么.以及这些技术是如何演变的.还有哪些事情需要改进?
我一直试图谷歌我的问题,但老实说,我不知道如何简洁地陈述问题.
假设我在多核Intel系统中有两个线程.这些线程在同一个NUMA节点上运行.假设线程1写入X一次,然后只是偶尔读取它向前移动.进一步假设,线程2连续读取X. 如果我不使用内存栅栏,在线程1写入X和线程2看到更新值之间可以有多长时间?
我知道X的写入将转到存储缓冲区并从那里到缓存,此时MESIF将启动,线程2将通过QPI查看更新的值.(或者至少这是我收集到的).我假设存储缓冲区将被写入存储围栏中的缓存或者是否需要重用该存储缓冲区条目,但我不知道存储缓冲区是否已分配给写入.
最终我要为自己回答的问题是,如果线程2有可能在一个相当复杂的应用程序中看到线程1的写入几秒钟而正在做其他工作.
我正在阅读英特尔指令集指南 64-ia-32指南 以了解内存栅栏。我的问题是,以 SFENCE 为例,为了确保所有存储操作都是全局可见的,多核 CPU 是否会停放所有线程甚至在其他内核上运行,直到实现缓存一致性?
x86 multithreading multicore cpu-architecture memory-barriers
我看到g ++生成一个简单的movfor x.load()和mov+ mfencefor x.store(y).考虑这个经典的例子:
#include<atomic>
#include<thread>
std::atomic<bool> x,y;
bool r1;
bool r2;
void go1(){
x.store(true);
}
void go2(){
y.store(true);
}
bool go3(){
bool a=x.load();
bool b=y.load();
r1 = a && !b;
}
bool go4(){
bool b=y.load();
bool a=x.load();
r2= b && !a;
}
int main() {
std::thread t1(go1);
std::thread t2(go2);
std::thread t3(go3);
std::thread t4(go4);
t1.join();
t2.join();
t3.join();
t4.join();
return r1*2 + r2;
}
Run Code Online (Sandbox Code Playgroud)
其中根据https://godbolt.org/z/APS4ZY go1和go2被翻译成
go1():
mov BYTE PTR x[rip], 1 …Run Code Online (Sandbox Code Playgroud) 正如我从测试用例中看到的:https : //godbolt.org/z/K477q1
生成的程序集加载/存储原子放松与普通变量相同:ldr 和 str
那么,松弛原子变量和普通变量之间有什么区别吗?
鉴于 CPU 现在是多核的并且有自己的 L1/L2 缓存,我很好奇 L3 缓存是如何组织的,因为它由多个内核共享。我会想象,如果我们有 4 个内核,那么 L3 缓存将包含 4 个页面的数据,每个页面对应于特定内核引用的内存区域。假设我有点正确,就这样吗?例如,它可以将这些页面中的每一个划分为子页面。这样,当多个线程在同一个内核上运行时,每个线程都可以在其中一个子页面中找到它们的数据。我只是突然想到了这个,所以我非常有兴趣让自己了解幕后真正发生的事情。任何人都可以分享他们的见解或为我提供一个链接来治愈我的无知吗?
提前谢谢了。
由于其 TSO 内存模型,X86 保证所有商店的总顺序。我的问题是是否有人知道这是如何实际实施的。
我对所有 4 个围栏是如何实现的印象很好,所以我可以解释如何保留本地秩序。但是 4 个栅栏只会给 PO;它不会给您 TSO(我知道 TSO 允许旧商店跳到新负载前面,因此只需要 4 个围栏中的 3 个)。
单个地址上所有内存操作的总顺序是一致性的责任。但我想知道英特尔(特别是 Skylake)如何在多个地址的商店上实现总订单。
x86 intel cpu-architecture memory-barriers micro-architecture
x86 ×7
c++ ×4
intel ×4
atomic ×3
concurrency ×3
cpu-cache ×2
stdatomic ×2
amd ×1
assembly ×1
c++11 ×1
cpu ×1
gcc ×1
lockless ×1
memory-model ×1
multicore ×1
numa ×1
optimization ×1
performance ×1
volatile ×1