为什么 _mm_mfence() 会为 ALL_LOADS 性能事件生成计数?
Why does _mm_mfence() produce counts for the ALL_LOADS perf event?
我正在测试一些内部操作的行为。当我注意到 _mm_mfence() 从用户 space 发出加载指令时,我感到很惊讶,但它不计入 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
指令 ,从架构上讲,这不是加载或存储
它解码为的一个或多个微指令可能 微架构 运行 在加载端口上并被计为负载。
你在用什么CPU?如果是 Skylake,我假设您已经更新了微码,因此 mfence
比 Agner Fog 的表格列出的成本更高。 (并且它阻止了非内存 uops 的乱序执行,例如 lfence
。参见 Are loads and stores the only instructions that gets reordered? 显然在 Skylake 没有为 mfence
.)
我正在测试一些内部操作的行为。当我注意到 _mm_mfence() 从用户 space 发出加载指令时,我感到很惊讶,但它不计入 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
指令 ,从架构上讲,这不是加载或存储
它解码为的一个或多个微指令可能 微架构 运行 在加载端口上并被计为负载。
你在用什么CPU?如果是 Skylake,我假设您已经更新了微码,因此 mfence
比 Agner Fog 的表格列出的成本更高。 (并且它阻止了非内存 uops 的乱序执行,例如 lfence
。参见 Are loads and stores the only instructions that gets reordered? 显然在 Skylake 没有为 mfence
.)