我正在测试一些内在操作的行为。当我注意到_mm_mfence()从用户空间发出加载指令时,我感到很惊讶,但它不计入L1数据缓存 - 未命中,命中或填充缓冲区命中。我正在使用papi的本机事件(如MEM_INST_RETIRED和MEM_LOAD_RETIRED)来读取性能计数器。这段代码:
for(int i=0; i < 1000000; i++){
_mm_mfence();
}
计数ALL_LOADS:737030,L1_HIT:99,L1_MISS:10,FB_HIT:25。虽然没有mfence,读取计数器的开销是这样的:ALL_LOADS:125,L1_HIT:94,L1_MISS:11,FB_HIT:24
我查了一下,sfence和lfence没有这种影响。我正在使用-O3进行编译。从编译文件我想它调用__builtin_ia32_mfence函数,但我找不到它。
我一般都了解_mm_mfence()的作用以及我们使用它的原因,但现在问题更多的是它是如何工作的。如果有人能够解释或给出任何相关文章来理解这种行为,那将是很棒的。
_mm_mfence()
编译成mfence
指令,这不是装载或存储,从架构上讲
但是,它解码到的一个或多个uop可能会在一个加载端口上进行微体系结构运行并被计为负载。
你用的CPU是什么?如果Skylake,我认为你已经更新了微码,所以mfence
的费用高于Agner Fog的表格。 (并且它阻止了非内存uops的无序执行,如lfence
。请参阅Are loads and stores the only instructions that gets reordered?显然在Skylake没有为mfence
执行此操作之前的一些Intel CPU。)