一般地,对于int num,num++(或++num),作为读-修改-写操作中,是不是原子.但我经常看到编译器,例如GCC,为它生成以下代码(在这里尝试):
由于第5行对应于num++一条指令,我们可以得出结论,在这种情况下num++ 是原子的吗?
如果是这样,是否意味着如此生成num++可以在并发(多线程)场景中使用而没有任何数据争用的危险(例如,我们不需要制作它,std::atomic<int>并强加相关成本,因为它是无论如何原子)?
UPDATE
请注意,这个问题不是增量是否是原子的(它不是,而且是问题的开头行).它是否可以在特定场景中,即在某些情况下是否可以利用单指令性质来避免lock前缀的开销.而且,作为公认的答案约单处理器的机器,还有部分提到这个答案,在其评论和其他人谈话解释,它可以(尽管不是C或C++).
如果有两个线程访问全局变量,那么许多教程都说使变量volatile变为阻止编译器将变量缓存在寄存器中,从而无法正确更新.但是,访问共享变量的两个线程是通过互斥锁来调用保护的东西不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有那个线程可以访问变量,在这种情况下变量不需要是volatile?
那么多线程程序中volatile的用途/目的是什么?
我编写了一个简单的多线程程序,如下所示:
static bool finished = false;
int func()
{
size_t i = 0;
while (!finished)
++i;
return i;
}
int main()
{
auto result=std::async(std::launch::async, func);
std::this_thread::sleep_for(std::chrono::seconds(1));
finished=true;
std::cout<<"result ="<<result.get();
std::cout<<"\nmain thread id="<<std::this_thread::get_id()<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它通常表现在调试模式下在Visual Studio中或-O0在GC c和后打印出的结果1秒钟。但是它卡住了,在“ 释放”模式或中不打印任何内容-O1 -O2 -O3。
考虑像一个数组atomic<int32_t> shared_array[].如果你想SIMD矢量化for(...) sum += shared_array[i].load(memory_order_relaxed)怎么办?或者在数组中搜索第一个非零元素,或者将其范围归零?这可能很少见,但考虑一下不允许在元素内撕裂的任何用例,但在元素之间重新排序很好. (也许是寻找CAS候选人的搜索).
我认为 x86对齐的向量加载/存储在实践中可以安全地用于带有mo_relaxed操作的SIMD ,因为任何撕裂只会发生在当前硬件上最坏的8B边界(因为这是自然对齐的8B访问原子1的原因).不幸的是,英特尔的手册只说:
"可以使用多个存储器访问来实现访问大于四字的数据的x87指令或SSE指令."
无法保证这些组件访问是自然对齐,不重叠或其他任何内容.(有趣的事实:根据Agner Fog,大概是qword + word,fld m80在Haswell上用2个加载uops和2个ALU uops完成x87 10字节加载.)
如果你想在面向未来的方式,当前的x86手册上说,未来所有的x86 CPU将努力向量化,你可以在8B块与加载/存储movq/ movhps.
或者你可以使用vpmaskmovd带有全真掩码的256b,因为手册的操作部分用多个独立的32位负载来定义它,比如Load_32(mem + 4).这是否意味着每个元素都作为一个单独的32位访问,保证该元素内的原子性?
(在实际硬件上,它是Haswell上的1个负载和2个端口5 uops,或者Ryzen上只有1或2个负载+ ALU uops(128/256).我认为这是针对不需要从元素中抑制异常的情况进入一个未映射的页面,因为它可能会更慢(但IDK如果它需要微代码辅助).无论如何,这告诉我们它至少与vmovdqaHaswell上的正常负载一样原子,但这告诉我们没有关于x86 Deathstation 9000 16B的信息/ 32B向量访问被分解为单字节访问,因此每个元素内可能会有撕裂.
我认为实际上可以安全地假设你不会在16,32或64位元素中撕裂任何真正的x86 CPU上的对齐矢量加载/存储,因为这对于已经有效的实现是没有意义的必须保持自然对齐的64位标量存储原子,但知道手册中的保证到底有多远是有趣的.)
收集(AVX2,AVX512)/ Scatter(AVX512)
类似vpgatherdd的指令显然由多个独立的32b或64b访问组成.AVX2表格被记录为多次执行,FETCH_32BITS(DATA_ADDR);因此可能会被通常的原子性保证所覆盖,并且如果它不跨越边界,则每个元素将以原子方式收集.
AVX512褶裥都记录在英特尔公司的PDF的insn参考手册作为
DEST[i+31:i] <- MEM[BASE_ADDR + SignExtend(VINDEX[i+31:i]) * …