我有一个64字节大小的对象:
typedef struct _object{
int value;
char pad[60];
} object;
主要是我正在初始化对象数组:
volatile object * array;
int arr_size = 1000000;
array = (object *) malloc(arr_size * sizeof(object));
for(int i=0; i < arr_size; i++){
array[i].value = 1;
_mm_clflush(&array[i]);
}
_mm_mfence();
然后再次遍历每个元素。这是我正在为以下事件计数的循环:
int tmp;
for(int i=0; i < arr_size-105; i++){
array[i].value = 2;
//tmp = array[i].value;
_mm_mfence();
}
在这里拥有mfence毫无意义,但我在捆绑其他东西,无意间发现,如果我进行了商店操作而没有mfence ,我将收到50万次RFO请求(以papi L2_RQSTS.ALL_RFO衡量)事件),这意味着又有50万首L1命中,是在需求之前预取的。但是包括mfence 会导致一百万个RFO请求,从而产生RFO_HIT,这意味着缓存行仅在L2缓存中预取,而不再在L1缓存中预取。
除了英特尔文档以某种方式另外说明这一事实:“可以在执行MFENCE指令之前,之中或之后将数据推测性地带入缓存”。我用 load操作进行了检查。。如果没有mfence,我的命中率最高为2000 L1,而通过mfence,我的命中率最高为100万L1(以papi MEM_LOAD_RETIRED.L1_HIT事件衡量)。缓存行已在L1中预取以用于加载指令。
因此,不应该包括mfence块预取。存储和加载操作几乎都需要花费相同的时间-不需5-6毫秒,而需20毫秒。我经历了有关mfence的其他问题,但未提及预取对它的预期行为,我没有看到足够好的理由或解释,为什么它仅通过存储操作会阻止L1缓存中的预取。还是我可能缺少某些功能描述?
我正在Skylake微体系结构上进行测试,但是与Broadwell进行了核对,并获得了相同的结果。
答案 0 :(得分:3)
不是L1预取会导致您看到计数器值:即使禁用L1预取器,效果仍然存在。实际上,如果禁用除L2流媒体之外的所有预取器,效果仍然存在:
wrmsr -a 0x1a4 "$((2#1110))"
但是,如果您要做禁用了L2流光,则计数与您期望的一样:即使没有{{1,您也会看到大约1,000,000 L2.RFO_MISS
和L2.RFO_ALL
}}。
首先,请注意mfence
事件计数不对源自L2流媒体的RFO事件计数。您可以看到详细信息here,但是基本上每个0x24 RFO事件的umask都是:
L2_RQSTS.RFO_*
请注意,所有umask值都没有name umask
RFO_MISS 0x22
RFO_HIT 0x42
ALL_RFO 0xE2
位,该位指示应该跟踪源自L2流媒体的事件。
似乎发生的事情是,当L2流媒体处于活动状态时,您可能希望分配给其中一个事件的许多事件被L2预取器事件“吃掉”了。可能发生的情况是L2预取器在请求流之前运行,并且当需求RFO来自L1时,它发现已经从L2预取器进行了请求。这只会再次增加事件的0x10
版本(实际上,包括该位时,我总共获得2,000,000个引用),这意味着umask |= 0x10
和RFO_MISS
和RFO_HIT
会错过它
这有点类似于“ fb_hit”方案,在该方案中,L1既未加载也未命中,但未加载,但遇到了正在进行的加载-但复杂的是加载是由L2预取器启动的。
RFO_ALL
的速度降低得足够慢,以至于L2预取器几乎总是有时间将线路一直拉到L2,从而得到mfence
计数。
我认为这里根本不涉及L1预取器(事实是,如果您将它们关闭,则效果相同):据我所知,L1预取器不与商店交互,仅加载。
以下是一些有用的RFO_HIT
命令,您可以用来查看包含“ L2流媒体起源”位的区别。这里不包含L2流媒体事件:
perf
,其中包括:
perf stat --delay=1000 -e cpu/event=0x24,umask=0xef,name=l2_rqsts_references/,cpu/event=0x24,umask=0xe2,name=l2_rqsts_all_rfo/,cpu/event=0x24,umask=0xc2,name=l2_rqsts_rfo_hit/,cpu/event=0x24,umask=0x22,name=l2_rqsts_rfo_miss/
我针对此代码运行了这些命令(perf stat --delay=1000 -e cpu/event=0x24,umask=0xff,name=l2_rqsts_references/,cpu/event=0x24,umask=0xf2,name=l2_rqsts_all_rfo/,cpu/event=0x24,umask=0xd2,name=l2_rqsts_rfo_hit/,cpu/event=0x24,umask=0x32,name=l2_rqsts_rfo_miss/
与传递给perf的sleep(1)
命令对齐以排除初始化代码)
--delay=1000
答案 1 :(得分:1)
关于存储操作的情况,我已经在Haswell处理器上以四种不同的配置运行了相同的循环:
MFENCE
指令。所有硬件预取器均已启用。MFENCE
。所有硬件预取器均已启用。MFENCE
指令。所有硬件预取器均已禁用。MFENCE
。所有硬件预取器均已禁用。结果显示如下,这些结果通过存储数量(每个存储都位于不同的缓存行)中进行了标准化。它们在多次运行中具有确定性。
| MFENCE + E | E | MFENCE + D | D |
L2_RQSTS.ALL_RFO | 0.90 | 0.62 | 1.00 | 1.00 |
L2_RQSTS.RFO_HIT | 0.80 | 0.12 | 0.00 | 0.00 |
L2_RQSTS.RFO_MISS | 0.10 | 0.50 | 1.00 | 1.00 |
OFFCORE_REQUESTS.DEMAND_RFO | 0.20 | 0.88 | 1.00 | 1.00 |
PF_L3_RFO | 0.00 | 0.00 | 0.00 | 0.00 |
PF_RFO | 0.80 | 0.16 | 0.00 | 0.00 |
DMND_RFO | 0.19 | 0.84 | 1.00 | 1.00 |
前四个事件是核心事件,后三个事件是非核心响应事件:
L2_RQSTS.ALL_RFO
:针对向L2发出的每个RFO请求发生。这包括来自已退休或以其他方式退休的商店的RFO请求,以及来自PREFETCHW
的RFO请求。对于启用了硬件预取器的情况,事件计数少于预期的数目,这是标准化的数目。可以想到两个可能的原因:(1)某种形式的RFO在L1中命中,以及(2)事件被低估了。我们将通过检查其他事件的计数并回顾我们对L1D预取器的了解,来尝试找出原因。L2_RQSTS.RFO_HIT
和L2_RQSTS.RFO_MISS
:发生在LFO中分别命中或未命中的RFO。在所有配置中,这些事件的计数之和等于L2_RQSTS.ALL_RFO
。OFFCORE_REQUESTS.DEMAND_RFO
:此事件的文档建议它应与L2_RQSTS.RFO_MISS
相同。但是,请注意OFFCORE_REQUESTS.DEMAND_RFO
和L2_RQSTS.RFO_HIT
的总和实际上等于1。因此,L2_RQSTS.RFO_MISS
可能会被低估(因此L2_RQSTS.ALL_RFO
也会被低估)。实际上,这是最可能的解释,因为英特尔优化手册(和其他英特尔文档)说,只有L2流媒体预取器才能跟踪商店。英特尔性能计数器手册在L2_RQSTS.ALL_RFO
的描述中提到了“ L1D RFO预取”。这些预取信息可能是指尚未退休的商店中的RFO(请参阅对Why are the user-mode L1 store miss events only counted when there is a store initialization loop?的回答的最后部分)。PF_L3_RFO
:在触发来自L2流媒体预取器的RFO并且目标缓存结构仅是L3时发生。此事件的所有计数均为零。PF_RFO
:在触发来自L2流媒体预取器的RFO并且目标高速缓存结构是L2且可能是L3时发生(如果L3是含端点的,则该行也将被填充为L3好)。此事件的计数接近L2_RQSTS.RFO_HIT
。在MFENCE + E的情况下,似乎100%的RFO已按时完成(在需求RFO达到L2之前)。在E情况下,25%的预取未按时完成或预取了错误的行。与MFENCE + E情况相比,L2中的RFO命中次数要多于E情况的原因是MFENCE
指令延迟了以后的RFO,从而使L2的大多数超级队列条目可用于L2流媒体预取器。因此MFENCE
确实使L2流媒体预取器性能更好。如果没有它,L2上将有许多飞行中的需求RFO,从而留下了少量的用于预取的超级队列条目。DMND_RFO
:与OFFCORE_REQUESTS.DEMAND_RFO
相同,但看起来它可能被低估了。我检查了装载操作。没有mfence我最多可以升至2000 L1 打,而有了mfence,我的L1打达到一百万 papi MEM_LOAD_RETIRED.L1_HIT事件)。缓存行已预取 L1用于加载指令。
关于装入操作的情况,以我的经验,MFENCE
(或任何其他fence指令)对硬件预取器的行为没有影响。这里的MEM_LOAD_RETIRED.L1_HIT
事件的真实计数实际上很小(<2000)。正在计数的大多数事件来自MFENCE
本身,而不是负载。 MFENCE
(和SFENCE
)要求一直向存储控制器发送防护请求,以确保所有未决存储都已到达全局观察点。防护请求不算作RFO事件,但可能被算作多个事件,包括L1_HIT
。有关此以及类似观察的更多信息,请参阅我的博客文章:An Introduction to the Cache Hit and Miss Performance Monitoring Events。