为什么mem_load_retired.l1_hit和mem_load_retired.l1_miss没有添加到加载总数中?

问题描述 投票:0回答:1

我正在研究缓存对 x86-64 CPU 性能的影响。我一直在使用 Linux 的 perf 来监控缓存命中/未命中率,特别是这些计数器:

  • mem_inst_retired.all_loads
  • mem_load_retired.l1_hit
  • mem_load_retired.l1_miss

我希望如此,因为加载指令可能会命中或错过 L1 缓存 - 没有其他选择,因为所有常规加载都会通过缓存。我已经阅读了英特尔文档

here
,据我所知,其中并没有说太多内容,也没有任何内容可以反驳我的想法。 但是,当运行某些代码时,我注意到总和远低于

all_loads ~= l1_hit + l1_miss

例如,以下程序集将 10 亿个内存位置与步长 32B 相加:

all_loads

它产生以下性能结果:

.intel_syntax noprefix .globl main main: sub rsp, 8 # Stack alignment for call push r12 push r13 mov r12, 0xfffffff # Array size mov rdi, r12 call malloc # Allocate array mov r13, rax # Save array pointer mov rdi, r13 # Write to array to force page-in mov rsi, 42 mov rdx, r12 call memset mov rcx, 1000000000 # Loop counter mov rdx, 0 # Index sequence start mov rax, 0 # Result accumulator .p2align 4 # Skylake JCC alignment issue loop: mov rdi, rdx and rdi, r12 # Mask index to array size movzx rsi, BYTE PTR [r13+rdi] # Read from array index add rax, rsi lea rdx, [rdx+32] # Generate next array index dec rcx # Loop counter & condiiton jnz loop pop r13 pop r12 add rsp, 8 ret

正如预期的那样,
~$ perf stat -e instructions,cycles,mem_inst_retired.all_loads,mem_load_retired.l1_hit,mem_load_retired.l1_miss ./test

 Performance counter stats for './test':

     7,000,215,742      instructions:u                   #    1.25  insn per cycle            
     5,589,048,737      cycles:u                                                              
       998,622,922      mem_inst_retired.all_loads:u                                          
        17,215,080      mem_load_retired.l1_hit:u                                             
       424,118,595      mem_load_retired.l1_miss:u                                            

       1.939187022 seconds time elapsed

       1.826889000 seconds user
       0.112177000 seconds sys

大约为 10 亿(尽管有点低,但可能是一些采样人工制品)。然而

all_loads
约为 4.5 亿 - 似乎约有 50% 的负载下落不明。

是什么导致

l1_hit + l1_miss

l1_hit
之和不等于
l1_miss

有趣的是,如果内存负载跨度发生变化,使得几乎所有负载都命中或未命中,则结果趋向于

all_loads

。只有在中间地带,平等才会被打破。

编辑:我在两个 CPU 上进行了测试:Kaby Lake 和 Ice Lake。两者显示相同的结果。

caching x86 x86-64 cpu-architecture perf
1个回答
0
投票
正如 Margaret Bloom 在评论中指出的那样,加载到相同的缓存行,因为已经未完成的缓存行可以“命中”该 LFB

,而不是分配新的缓存行。事实证明,这既不是 all_loads ~= l1_hit + l1_miss 也不是

l1_hit
。还有一个单独的活动,
l1_miss
。 (
mem_load_retired.fb_hit
只计算导致向 L2 发出新请求的指令,而不是同时计算 LFB 命中,这可能是件好事。另请注意,LFB 可能会被传出存储占用,包括 NT 存储,因此会导致 LFB 命中也是可能的;这并不总是只是由于多次负载所致。)
您的代码跨度为 32 字节,因此每 64 字节行加载 2 次;第二个通常是 LFB 热门歌曲。 (如果硬件预取已经请求,第一个也可能是 LFB 命中,这可能解释了 LFB 命中多于未命中的原因。)

在我的 Skylake i7-6700k 上使用此测试程序,

l1_miss

仅比

mem_inst_retired.all_loads
大 0.6% 左右。
因此,其中的区别是什么,

mem_load_retired.fb_hit + mem_load_retired.l1_hit + mem_load_retired.l1_miss

计数的内容,而不是三个更具体的计数器中的任何一个,仍然存在一些谜团。我原以为它们会更接近完全相等,特别是对于

mem_inst_retired.all_loads
--all-user
事件,这样在对计数器进行编程或收集时就不会产生噪音。
使用 

:u

可以轻松地将数字复制/粘贴到

perf stat --no-big-num --all-user -e ...
中,我在一次运行中得到了 hit+miss+LFB = 994.188M vs. all-loads = 999.958M 计数。所以总和低了 0.58%。在重复运行时,这是非常典型的,未命中/命中/LFB 计数器的总和比 mem_inst_retired.all_loads 低一小部分。
再跑几次:

calc

我的 EPP 
$ perf stat --all-user --no-big-num -e task-clock,page-faults,instructions,cycles,mem_inst_retired.all_loads,mem_load_retired.l1_hit,mem_load_retired.l1_miss,mem_load_retired.fb_hit ./a.out Performance counter stats for './a.out': 1673.21 msec task-clock # 0.997 CPUs utilized 183 page-faults # 109.371 /sec 7000141672 instructions # 1.56 insn per cycle 4475942186 cycles # 2.675 GHz 999892622 mem_inst_retired.all_loads # 597.590 M/sec 10563966 mem_load_retired.l1_hit # 6.314 M/sec 449822478 mem_load_retired.l1_miss # 268.838 M/sec 533816318 mem_load_retired.fb_hit # 319.038 M/sec 1.677680356 seconds time elapsed 1.647640000 seconds user 0.023197000 seconds sys $ perf stat --all-user --no-big-num -e task-clock,page-faults,instructions,cycles,mem_inst_retired.all_loads,mem_load_retired.l1_hit,mem_load_retired.l1_miss,mem_load_retired.fb_hit ./a.out Performance counter stats for './a.out': 1649.17 msec task-clock # 1.000 CPUs utilized 182 page-faults # 110.359 /sec 7000141486 instructions # 1.58 insn per cycle 4419739785 cycles # 2.680 GHz 999850616 mem_inst_retired.all_loads # 606.275 M/sec 9372903 mem_load_retired.l1_hit # 5.683 M/sec 450146459 mem_load_retired.l1_miss # 272.953 M/sec 534244404 mem_load_retired.fb_hit # 323.947 M/sec 1.649504255 seconds time elapsed 1.634275000 seconds user 0.013270000 seconds sys

)设置为

/sys/devices/system/cpu/cpufreq/policy*/energy_performance_preference
(未满
balance-performance
),因此硬件 P 状态管理在此内存密集型工作负载上的时钟频率降至 2.7GHz。 (这在一定程度上减少了每核内存带宽,因为非核心速度减慢,使得延迟 x max_in-flight_lines 成为单核内存带宽的限制因素。这对于本实验来说不是问题,但它是其他不明显但可见的问题在这个性能数据中。我的 i7-6700k 有双通道 DDR4-2666,运行 Arch Linux,内核 6.4.9)
    

© www.soinside.com 2019 - 2024. All rights reserved.