mini bench IsaDiffDepen
用例标签
使用一个规则随机读写一个大小为 8MB 的空间,执行 4096 轮,每轮包含一次读写
用例软件行为描述
#define AMULT 65536
#define ASIZE 32
#define AMASK (ASIZE-1)
#define ITERS 4096
int arr1[ASIZE*AMULT];
int loop(int zero) {
int acc1 = 1, count=0;
int acc2 = 2;
unsigned lfsr = 0xACE1u;
unsigned lfsr2 = 0x0143u;
do
{
/* taps: 16 14 13 11; feedback polynomial: x^16 + x^14 + x^13 + x^11 + 1 */
lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xB400u);
acc1 += arr1[(lfsr&AMASK)*AMULT];
lfsr2 = (lfsr2 >> 1) ^ (-(lfsr2 & 1u) & 0xB400u);
acc2+=1;
arr1[(lfsr2&AMASK)*AMULT]=acc2;
} while(++count < ITERS);
return acc1;
}
80000038: 00175893 srli a7,a4,0x1
8000003c: 01f8f613 andi a2,a7,31
80000040: 0017d813 srli a6,a5,0x1
80000044: 064a slli a2,a2,0x12
80000046: 9672 add a2,a2,t3
80000048: 01f87693 andi a3,a6,31
8000004c: 00062e83 lw t4,0(a2)
80000050: 8b05 andi a4,a4,1
80000052: 8b85 andi a5,a5,1
80000054: 06ca slli a3,a3,0x12
80000056: 2585 addiw a1,a1,1
80000058: 40e0073b negw a4,a4
8000005c: 40f007bb negw a5,a5
80000060: 96f2 add a3,a3,t3
80000062: 00e37733 and a4,t1,a4
80000066: 00f377b3 and a5,t1,a5
8000006a: c28c sw a1,0(a3)
8000006c: 00e8c733 xor a4,a7,a4
80000070: 00ae853b addw a0,t4,a0
80000074: 00f847b3 xor a5,a6,a5
80000078: fde590e3 bne a1,t5,80000038 <loop+0x1e>
只会访问这 8MB 空间内的特定的 32 个位置(A,A+655364,A+(65536 * 2)4,A+(65536 * 3)4 ... A+(65536 * 31)4),执行 4096 轮,每轮先读后写,读写之间没有依赖关系,但访存地址可能相同,访存地址会依赖于当前这轮的 lfsr,当前这轮的 lfsr 依赖上一轮的 lfsr
这个测试在 L1 和 L2 访问同一个cache set,cache way < 32 因此导致 cache miss
在 L3 访问 4 个 cache set(L3 16MB,4 slice,16 路,每一路有 4K 个 set),在 L3 不会造成 cache miss
在 RTL 上 L2 L3 将访问同一个slice(地址 A 所在的 slice),并且会出现 set 阻塞的情况
为了减小干扰,以下测试每一个 iteration 会向前执行4096 轮,最终统计本轮 iteration 减去上一轮 iteration 的数据作为本轮的结果
理想IPC
https://gcc.godbolt.org/z/K9vcn586q:

使用 llvm-mca 这个工具静态分析出的理想 IPC 为 4.67(使用方法)
理想模型IPC
(idealkmhv3.py)(commit: 0a79459c73a8)

模型IPC
(kmhv3.py)(commit: 0a79459c73a8)

RTL IPC
(开预取器)(commit: cb62726bb)

DIFF:模型理想IPC以及RTL IPC的差异
RTL 的 IPC 稳定在 0.54,GEM5 的 IPC 稳定在 3.2
mismatch描述:什么原因造成了现在这个差异
RTL 一些关键数据:

- Sq full rate 很低,说明 store 反压流水线的情况不存在
- L3 Hit Rate 比较高,L2 L1 的 hit rate 都较低,说明 L3 16 路的 cache 可以兜住这 32 个访问(在 L3 访问 4 个 set),L1 4 way,L2 8 way 兜不住这 32 个块访问
- AVG LoadMissPenalty 为 load 申请 mshr 后,到获得数据的延迟,平均 Load miss 后产生的 penalty 有 610 cycle,这个数字过大,在 L3 Hit 的情况下不应该有这么高的延迟,显然可能造成瓶颈
再看看 GEM5 的数据:

- miss rate 与 RTL 基本可以对应上

- GEM5 的 miss latency 是 15465 / 333 = 46 拍,大概为 L2 miss L3 hit 的延迟,达到这种延迟才算是正常的
观察 RTL 的波形,查看是否存在 Load miss penalty 过大的情况:

图中M1为Dcache发请求,M2为Dcache收到响应,中间隔了401拍,可以印证性能计数器
另外从波形中可以发现一个很重要的信息:L2 A 通道的 ready 长期处于拉低的状态,L3 A 通道的 ready 也长期处于拉低的状态,L2 A 通道拉低会导致 L1 这边请求长期发不出去,L3 A 通道 ready 长期拉低会导致 L2 这边请求长期发不出去,最终都会反应在 L1 的 miss latency 计数器中
做一些其他实验来辅助分析:如果把测试中的 ASIZE 设置为 4,只会访问 4 个 cache block,在 RTL 上和模型上将不会产生 cache miss
ASIZE 设置为 4,GEM5 ideal 跑的结果:


说明这个测试中 load miss 的延迟在 GEM5 上不是瓶颈,并不会因为 load miss 降低并行度,如果 GEM5 后端调度没有问题,理想 IPC 就是 3.2 ?(至少RTL理想IPC至少能达到这个数字)
ASIZE 设置为 4,RTL 跑的结果:


可以稳定在 3.2 说明 RTL IPC 0.5 到 3.2 的 gap 差距在 L1 miss 的情况下 L2 L3 的处理延迟上,也就是在访问同一个 slice,同一个 set 下 L2 L3 的访存带宽和延迟较差
ASIZE 设置为 8,RTL 跑的结果:



在ASIZE为 8 的情况下,RTL 的 L1 只有 4 way,L2 有 8 way,L2 可以兜住所有 8 次访问,但会出现 L1 miss,L2 hit 的情况,出现类似于 indepma_mim 的情况(L2 访问同一个 slice 的带宽出现不足的情况),即使是这种情况,平均的 load miss penalty 也只有 20,IPC 仍然可以达到 3.2,说明瓶颈不在 L2 而是主要在 L3
结论:
RTL 无法达到理想 IPC 的原因为:L2 miss,L3 hit 的情况下,请求集中访问L3同 slice,某几个 set时,L3 存在带宽不足与延迟过高的情况

波形中 L3 呈现出平均 68 个 cycle 才接收两个请求的规律
assignee: @zhenhao @lixin
RTL 性能瓶颈分析 @chenxi
小机房 /nfs-nvme/home/share/lixin/2024-12-23@16:01:50.vcd.vpd

通过对波形的进一步分析,得出如下结论:
- 首先明确,L3 为了简化在 Non-Inclusive 策略下的设计复杂度,采用了以 set 为粒度的阻塞方案
而 set 阻塞的粒度,并非是 L3 的 set,而是 L2 的 set
也就是,对于一系列【在 L2 中 set 相同】的地址,它们在 L3 中是串行执行的
参考代码
MSHRAlloc

HuanCun

- 并且,L3 在收到的连续 Acquire 中间还会插入 Release
这是因为 L2 发生了替换,将替换块释放给 L3,所以 L3 还需要处理 Release;
且所有的 Acquire 和 Release 处理都是串行的,L3 只用到了一个 MSHR
- 最后,一个 hit 请求对 MSHR 的占用时间会比我们预想的更长(19-20 拍)
虽然 L3 hit 的 latency 没有这么长(L3 9拍返回第一个数据,11拍返回第二个数据)
(补充说明,L2 看到的是 11/13 拍,因为 L2-L3 中间有一级 buffer)

但是 L3 需要等待收到 GrantAck 后更新目录后才能释放 MSHR,增加了 MSHR 的占用时间

这就导致了吞吐量的进一步下降
参考代码
MSHR


补充内容:
trace 分析
perfCCT 使用方法:https://bosc.yuque.com/yny0gi/sggyey/mc8ybawwh10mg1zc
目前使用 perfCCT 可以把指令生命周期的各个阶段打出来,gem5 适配的比较好,RTL 可能有漏打错打和有时候对应不太上的问题。
gem5 的执行 trace:

在 gem5 上,load 基本上不会出现过高的执行延迟,而主要卡在 iq 没有发出去和在 ibuffer 中,主要瓶颈在后端(实际 ipc 为 3.2 和理想 ipc 4.67 的差距)
RTL 的执行 trace(这里 RTL perfCCT 有点问题,实际 gem5 的 0x8000004c pc 对应的是 RTL 上的 0x8000006c):
# 查看图像形式
# **RTL 注意 -p 为 1**
python3 perfcct.py build/2024-12-30@13:54:27.db --zoom 2.3 -p 1 -v | less

RTL 上 load 指令出现大量时间卡在 execute(一大串的 e),导致后续的指令都无法提交(一大串的 w),rob 满了后导致一直反压到前端(一大串的 f),RTL 和 gem5 的差距可能来源于 load 指令的执行时间过长
单独看这条 load 的执行情况:
# 查看文本形式,挑选查看 pc 为 0x8000006c 的 load 的执行情况
# **RTL 注意 -p 为 1**
python3 perfcct.py build/2024-12-30@13:54:27.db --zoom 2.3 -p 1 | grep "0x8000006c" | less

可以很明显地看到 load 的执行延迟有时候非常高,~50% 4 拍执行完成(load hit),剩下的执行延迟都高于 200 拍(cache miss/lsu 内部的阻塞)。