飞腾CPU体系结构(十一)
1. 预取机制
飞腾CPU为了提升性能,支持从外部内存中预取数据到L1或L2缓存中。如果某个缓存块内容不在L1/L2缓存中,当该缓存块的某个地址单元被第一访问时,就会遇到内存墙,即飞腾CPU会从内存中将整个缓存块内容传输到缓存中。软件不能直接感知这种预取机制,但内存墙会导致第一次访问该缓存块的访存指令延迟比较大。
飞腾CPU还提供专门的预取指令PRFM,用于第一次访问新数据之前就提前通知缓存去内存预取相关缓存块内容,来避免内存墙延迟。
需要注意的是,这种预取指令PRFM只能用于
系统主存类型
,不能用于外设内存类型
。
2. 预取指令
除了数据地址之外,预取指令PRFM还指定了类型,目标和策略三种基本参数。
参数 | 说明 | |
类型 | pid | 为了load预期 |
pst | 为了store预期 | |
目标 | l1 | L1级缓存 |
l2 | L2级缓存 | |
策略 | keep | 常规缓存策略 |
strm | 流数据缓存策略 |
举个列子
指令 | 说明 |
prfm pldl1keep [x0] | 将缓存块从内存预读到L1缓存中,并会在读访问完成后,还会保存一段时间 |
prfm pldl2strm [x0] | 将缓存块从内存预读到L1缓存中,并会在读访问完成后,不久就失效了 |
3. 尤其要注意
不能想当然地认为,预取就是一定会优化,预期有时候不一定会有优化效果,使用不当还会造成性能损失。
4. copy_page汇编函数分析
4.1 输入参数描述
X0 | 目标虚拟地址 |
X1 | 源虚拟地址 |
默认参数为页长度参数,PAGE_SIZE
4.2 函数基本流程
- 先处理从源的第一个128字节的读
- 写上一个128字节/读下一个128字节的交替
- 最后处理到目标的最后128字节写
4.3 最原始的代码分析
ENTRY(copy_page)
第一部分:先处理从源的第一个128字节的读
函数一开始就预取第二个、第三个和第四个128字节,如果CPU硬件不支持预取指令,就用nop指令取代。
alternative_if ARM64_HAS_NO_HW_PREFETCH
prfm pldl1strm, [x1, #128]
prfm pldl1strm, [x1, #256]
prfm pldl1strm, [x1, #384]
alternative_else_nop_endif
第一个128字节的读取,这128字节没有必要预取,如果预取第一个128字节,性能不升反而下降
ldp x2, x3, [x1]
ldp x4, x5, [x1, #16]
ldp x6, x7, [x1, #32]
ldp x8, x9, [x1, #48]
ldp x10, x11, [x1, #64]
ldp x12, x13, [x1, #80]
ldp x14, x15, [x1, #96]
ldp x16, x17, [x1, #112]
/*x18用于记录还剩的字节数量*/
mov x18, #(PAGE_SIZE - 128)
add x1, x1, #128
下面属于第二部分了:写上一个128字节/读下一个128字节的交替
这部分是循环的主体部分
1:
subs x18, x18, #128
alternative_if ARM64_HAS_NO_HW_PREFETCH
prfm pldl1strm, [x1, #384]
/此处的预取数据为3个128字节之后的数据了/alternative_else_nop_endif
stnp x2, x3, [x0]
/先写,这属于上一个128字节内的范围/ldp x2, x3, [x1]
/后读,这属于下一个128字节内的范围/stnp x4, x5, [x0, #16]
ldp x4, x5, [x1, #16]
stnp x6, x7, [x0, #32]
ldp x6, x7, [x1, #32]
stnp x8, x9, [x0, #48]
ldp x8, x9, [x1, #48]
stnp x10, x11, [x0, #64]
ldp x10, x11, [x1, #64]
stnp x12, x13, [x0, #80]
ldp x12, x13, [x1, #80]
stnp x14, x15, [x0, #96]
ldp x14, x15, [x1, #96]
stnp x16, x17, [x0, #112]
ldp x16, x17, [x1, #112]
add x0, x0, #128
add x1, x1, #128
b.gt 1b
/跳转到标签1处/
最后第三部分:处理到目标的最后128字节写
完成后函数返回
stnp x2, x3, [x0]
stnp x4, x5, [x0, #16]
stnp x6, x7, [x0, #32]
stnp x8, x9, [x0, #48]
stnp x10, x11, [x0, #64]
stnp x12, x13, [x0, #80]
stnp x14, x15, [x0, #96]
stnp x16, x17, [x0, #112]
ret
ENDPROC(copy_page)