为什么有太多需求 rfo offcore 响应/offcore 请求?

Why are there too many demand rfo offcore responses /offcore requests?

Whiskey Lake i7-8565U/Ubuntu 18.04/HT enabled

考虑以下代码,将一些恰好在寄存器 ymm0ymm1 中的垃圾数据写入 16 MiB 静态分配的 WB 内存中由 6400 次迭代组成的循环(因此页面错误的影响可以忽略不计):

;rdx = 16MiB >> 3
xor rcx, rcx
store_loop:
vmovdqa [rdi + rcx*8], ymm0
vmovdqa [rdi + rcx*8 + 0x20], ymm1
add rcx, 0x08
cmp rdx, rcx
ja store_loop

使用 taskset -c 3 ./bin 我正在通过此示例测量 RFO 请求,结果如下:

Performance counter stats for 'taskset -c 3 ./bin':

     1 695 029 000      L1-dcache-load-misses     # 2325,60% of all L1-dcache hits    (24,93%)
        72 885 527      L1-dcache-loads                                               (24,99%)
     3 411 237 144      L1-dcache-stores                                              (25,05%)
       946 374 671      l2_rqsts.all_rfo                                              (25,11%)
       451 047 123      l2_rqsts.rfo_hit                                              (25,15%)
       495 868 337      l2_rqsts.rfo_miss                                             (25,15%)
     2 367 931 179      l2_rqsts.all_pf                                               (25,14%)
       568 168 558      l2_rqsts.pf_hit                                               (25,08%)
     1 785 300 075      l2_rqsts.pf_miss                                              (25,02%)
     1 217 663 928      offcore_requests.demand_rfo                                     (24,96%)
     1 963 262 031      offcore_response.demand_rfo.any_response                                     (24,91%)
           108 536      dTLB-load-misses          #    0,20% of all dTLB cache hits   (24,91%)
        55 540 014      dTLB-loads                                                    (24,91%)
        26 310 618      dTLB-store-misses                                             (24,91%)
     3 412 849 640      dTLB-stores                                                   (24,91%)
    27 265 942 916      cycles                                                        (24,91%)

       6,681218065 seconds time elapsed

       6,584426000 seconds user
       0,096006000 seconds sys

l2_rqsts.all_rfo的描述是

Counts the total number of RFO (read for ownership) requests to L2 cache. L2 RFO requests include both L1D demand RFO misses as well as L1D RFO prefetches.

建议 DCU 可以进行某种 RFO 预取。 Intel Optimization Manual/2.6.2.4:

对DCU的描述不清楚

Data cache unit (DCU) prefetcher — This prefetcher, also known as the streaming prefetcher, is triggered by an ascending access to very recently loaded data. The processor assumes that this access is part of a streaming algorithm and automatically fetches the next line.

所以我猜 DCU 遵循 "access type":如果是 RFO,那么 DCU 会进行 RFO 预取。

所有这些 RFO 预取都应该与需求 RFO 一起进入 L2,只有其中一些 (l2_rqsts.rfo_miss) 应该进入非核心。 offcore_requests.demand_rfo 仅计算需求 rfo,但 l2_rqsts.rfo_miss 计算所有 rfo(需求 + dcu prefectch),这意味着不等式 offcore_requests.demand_rfo < l2_rqsts.rfo_miss 应该成立。

问题 1: 为什么 l2_rqsts.rfo_missoffcore_requests.demand_rfo 少很多(甚至 l2_rqsts.all_rfooffcore_requests.demand_rfo 少)

我预计需求 offcore_requests.demand_rfo 可以与 offcore_response.demand_rfo.any_response 匹配,因此这些核心 PMU 事件的数量应该大致相等

问题 2: 为什么 offcore_response.demand_rfo.any_response 几乎是 offcore_requests.demand_rfo 的 1.5 倍?

我猜 L2-streamer 也会做一些 RFO 预取,但无论如何都不应该在 offcore_requests.demand_rfo 中考虑。


UPD:

$ sudo rdmsr -p 3 0x1A4
1

L2-Streamer 关闭

 Performance counter stats for 'taskset -c 3 ./bin':

     1 672 633 985      L1-dcache-load-misses     # 2272,75% of all L1-dcache hits    (24,96%)
        73 595 056      L1-dcache-loads                                               (25,00%)
     3 409 928 481      L1-dcache-stores                                              (25,00%)
     1 593 190 436      l2_rqsts.all_rfo                                              (25,04%)
        16 582 758      l2_rqsts.rfo_hit                                              (25,07%)
     1 579 107 608      l2_rqsts.rfo_miss                                             (25,07%)
       124 294 129      l2_rqsts.all_pf                                               (25,07%)
        22 674 837      l2_rqsts.pf_hit                                               (25,07%)
       102 019 160      l2_rqsts.pf_miss                                              (25,07%)
     1 661 232 864      offcore_requests.demand_rfo                                     (25,02%)
     3 287 688 173      offcore_response.demand_rfo.any_response                                     (24,98%)
           139 247      dTLB-load-misses          #    0,25% of all dTLB cache hits   (24,94%)
        56 823 458      dTLB-loads                                                    (24,90%)
        26 343 286      dTLB-store-misses                                             (24,90%)
     3 384 264 241      dTLB-stores                                                   (24,94%)
    37 782 766 410      cycles                                                        (24,94%)

       9,320791474 seconds time elapsed

       9,213383000 seconds user
       0,099928000 seconds sys

可以看出offcore_requests.demand_rfo接近了l2_rqsts.rfo_miss,但还是有一些区别。在 OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD 的英特尔文档中,我发现了以下内容:

Note: A prefetch promoted to Demand is counted from the promotion point.

所以我的猜测是 L2 预取被提升为需求并计入需求外核请求。但它并没有解释 offcore_response.demand_rfo.any_responseoffcore_requests.demand_rfo 之间的区别,后者现在几乎是两倍:

offcore_requests.demand_rfo 1 661 232 864

offcore_response.demand_rfo.any_response 3 287 688 173


更新:

$ sudo rdmsr -p 3 0x1A4
3

关闭所有 L2 预取器

 Performance counter stats for 'taskset -c 3 ./bin':

     1 686 560 752      L1-dcache-load-misses     # 2138,14% of all L1-dcache hits    (23,44%)
        78 879 830      L1-dcache-loads                                               (23,48%)
     3 409 552 015      L1-dcache-stores                                              (23,53%)
     1 670 187 931      l2_rqsts.all_rfo                                              (23,56%)
            15 674      l2_rqsts.rfo_hit                                              (23,59%)
     1 676 538 346      l2_rqsts.rfo_miss                                             (23,58%)
           156 206      l2_rqsts.all_pf                                               (23,59%)
            14 436      l2_rqsts.pf_hit                                               (23,59%)
           173 163      l2_rqsts.pf_miss                                              (23,59%)
     1 671 606 174      offcore_requests.demand_rfo                                     (23,59%)
     3 301 546 970      offcore_response.demand_rfo.any_response                                     (23,59%)
           140 335      dTLB-load-misses          #    0,21% of all dTLB cache hits   (23,57%)
        68 010 546      dTLB-loads                                                    (23,53%)
        26 329 766      dTLB-store-misses                                             (23,49%)
     3 429 416 286      dTLB-stores                                                   (23,45%)
    39 462 328 435      cycles                                                        (23,42%)

       9,699770319 seconds time elapsed

       9,596304000 seconds user
       0,099961000 seconds sys

现在对 l2 的预取请求总数(来自所有预取器)为 156 206 l2_rqsts.all_pf


UPD:

$ sudo rdmsr -p 3 0x1A4
7

̶A̶l̶l̶̶p̶r̶e̶f̶e̶t̶c̶h̶e̶r̶s̶̶t̶u̶r̶n̶e̶d̶̶o̶f̶f̶。̶仅启用IP预取

 Performance counter stats for 'taskset -c 3 ./bin':

     1 672 643 256      L1-dcache-load-misses     # 1893,36% of all L1-dcache hits    (24,92%)
        88 342 382      L1-dcache-loads                                               (24,96%)
     3 411 575 868      L1-dcache-stores                                              (25,00%)
     1 672 628 218      l2_rqsts.all_rfo                                              (25,04%)
            10 585      l2_rqsts.rfo_hit                                              (25,04%)
     1 684 510 576      l2_rqsts.rfo_miss                                             (25,04%)
            10 042      l2_rqsts.all_pf                                               (25,04%)
             4 368      l2_rqsts.pf_hit                                               (25,05%)
             9 135      l2_rqsts.pf_miss                                              (25,05%)
     1 684 136 160      offcore_requests.demand_rfo                                     (25,05%)
     3 316 673 543      offcore_response.demand_rfo.any_response                                     (25,05%)
           133 322      dTLB-load-misses          #    0,21% of all dTLB cache hits   (25,03%)
        64 283 883      dTLB-loads                                                    (24,99%)
        26 195 527      dTLB-store-misses                                             (24,95%)
     3 392 779 428      dTLB-stores                                                   (24,91%)
    39 627 346 050      cycles                                                        (24,88%)

       9,710779347 seconds time elapsed

       9,610209000 seconds user
       0,099981000 seconds sys

UPD:

$ sudo rdmsr -p 3 0x1A4
f

禁用所有预取器

 Performance counter stats for 'taskset -c 3 ./bin':

     1 695 710 457      L1-dcache-load-misses     # 2052,21% of all L1-dcache hits    (23,47%)
        82 628 503      L1-dcache-loads                                               (23,47%)
     3 429 579 614      L1-dcache-stores                                              (23,47%)
     1 682 110 906      l2_rqsts.all_rfo                                              (23,51%)
            12 315      l2_rqsts.rfo_hit                                              (23,55%)
     1 672 591 830      l2_rqsts.rfo_miss                                             (23,55%)
                 0      l2_rqsts.all_pf                                               (23,55%)
                 0      l2_rqsts.pf_hit                                               (23,55%)
                12      l2_rqsts.pf_miss                                              (23,55%)
     1 662 163 396      offcore_requests.demand_rfo                                     (23,55%)
     3 282 743 626      offcore_response.demand_rfo.any_response                                     (23,55%)
           126 739      dTLB-load-misses          #    0,21% of all dTLB cache hits   (23,55%)
        59 790 090      dTLB-loads                                                    (23,55%)
        26 373 257      dTLB-store-misses                                             (23,55%)
     3 426 860 516      dTLB-stores                                                   (23,55%)
    38 282 401 051      cycles                                                        (23,51%)

       9,377335173 seconds time elapsed

       9,281050000 seconds user
       0,096010000 seconds sys

即使禁用了预取器,perf 也将 12 报告为 pf_miss(可在具有不同值的不同运行中重现)。这可能是计数错误。 1 672 591 830 l2_rqsts.rfo_miss 的值也比 1 662 163 396 offcore_requests.demand_rfo 稍大,我也倾向于将其解释为计数错误。


假设: DCU RFO 预取缺少 L2 和离开核心在 offcore_requests.demand_rfo 中。

如果 L2-streamer 关闭,假设有效:102 019 160 l2_rqsts.pf_miss + 1 579 107 608 l2_rqsts.rfo_miss = 1 681 126 7681 661 232 864 offcore_requests.demand_rfo

如果所有预取器都关闭,该假设也成立:1 684 510 576 l2_rqsts.rfo_miss1 684 136 160 offcore_requests.

在所有 PF 关闭的情况下 L1-dcache-load-misses 约等于 l2_rqsts.rfo_miss 进而等于 offcore_requests.demand_rfo

我仍然不知道为什么 offcore_response.demand_rfo.any_responseoffcore_requests.demand_rfo

有更大的价值

在我看来,循环正在写入 2^18 个缓存行,并且有一个外部循环(问题中未显示)执行内部循环(显示的循环)6400 次。因此,需求 RFO 的预期总数为 2^18*6400 = 1,677,721,600,退休存储指令的预期数量为 1677721600*2 = 3,355,443,200。实测门店数量L1-dcache-stores约为34.1亿,比预期多出约5500万。这个事件计数应该是准确的,所以我假设还有其他代码没有显示在影响事件计数的问题中。负载事件计数还表明有很多负载来自某处,这对事件计数有重大影响 l2_rqsts.all_pfl2_rqsts.pf_hitl2_rqsts.pf_miss。我已经在我的评论中询问过测量中是否包含任何其他重要代码。

从启用所有预取器的第一个实验的结果来看,l2_rqsts.rfo_hit + offcore_requests.demand_rfo 加起来的数量几乎等于需求 RFO 的预期数量。 L2 streamer 实际上可以预取 RFO,如 Intel 优化手册中所述,该手册解释了如何可以 l2_rqsts.rfo_hit。我不知道为什么 l2_rqsts.rfo_miss 不等于 offcore_requests.demand_rfo。我认为事件 offcore_requests.demand_rfo 是准确的。尝试仅禁用 L1D 预取器并保持 L2 预取器处于启用状态并查看执行时间是否增加。如果 L1D 预取器实际上发送了任何数量的 RFO,则 L1D 中应该有足够的写入命中,从而在性能上产生差异。

禁用 L2 流光的第二个实验结果非常接近预期。 l2_rqsts.rfo_hit非常小,l2_rqsts.all_rfo几乎等于offcore_requests.demand_rfo,等于需求RFO的预期数量。这提供了 L1D 预取器不预取 RFO 的实验证据。 l2_rqsts.all_pf 在这种情况下应该为零,因为两个 L2 预取器都被禁用。

在上一个实验中,您只关闭了四个数据缓存预取器中的三个;您错过了 DCU IP 预取器。本例中 2_rqsts.all_rfo 的计数更接近例外情况。也尝试禁用 DCU IP 预取器并查看 l2_rqsts.rfo_hit(可能 l2_rqsts.all_pf)是否变为零。

Erratum 058 在您处理器的规范更新文档中说 offcore_response.demand_rfo.any_response 可能会多算,可以使用 offcore_requests.demand_rfo 代替。这解释了为什么 offcore_response.demand_rfo.any_response 比所有实验中的预期值都大,并且还表明 offcore_requests.demand_rfo 是可靠的。

对于问题 1,答案(至少在 Skylake 上是这样,但很可能与 Whiskey lake 相同)是 L2 RFO 事件在它们发生时不计算在内由预取启动:不是在预取被触发时,甚至是当 RFO 后来在 L2 中命中或未命中时。您可以通过设置事件的预取位(在 umask 中设置 0x10)来计算这些事件,在这种情况下,您会看到双重计数,如 .

您看到的事件是 RFO 的随机子集,其中 L2 预取器没有帮助。 offcore 计数器显然没有这样的问题:即使请求是由预取发起的,当需求命中正在进行的请求时,它也可以提升为需求请求。

您可以找到更多详细信息 here,并且您应该仔细检查您的 perf 版本使用了哪些事件,因为英特尔更改了最后一个 link 中描述的事件定义。