Zen3平台下RDRAND指令为何引发数据缓存访问与未对齐加载?
背景
我正在对一个大量使用rdrand指令的程序做基准测试和优化。排查疑似未对齐加载/存储导致的性能损耗时,发现ls_misal_loads.ma64(64字节未对齐加载)性能计数器数值异常偏高,而且这部分数值显然不是程序自身内存访问导致的,看起来和rdrand指令的执行次数直接相关。
另外,perf工具还报告了大量数据缓存访问,这些访问似乎也是由rdrand指令触发的。
测试程序与环境
极简测试程序(rd.asm)
bits 64 global main main: mov rdx, 1000000 ; 循环计数器 .loop: rdrand rax dec rdx jne .loop ret
编译命令
nasm -f elf64 rd.asm gcc rd.o
Perf测试结果
执行命令:
perf stat -e instructions,all_data_cache_accesses,ls_misal_loads.ma64 -- ./a.out
- 当计数器为1,000,000时:
Performance counter stats for './a.out': 3,666,525 instructions 24,422,483 all_data_cache_accesses 3,022,185 ls_misal_loads.ma64
- 当计数器为2,000,000时:
Performance counter stats for './a.out': 6,695,889 instructions 48,458,162 all_data_cache_accesses 6,016,069 ls_misal_loads.ma64
可以看到,rdrand执行次数翻倍后,数据缓存访问次数和未对齐加载次数也近乎翻倍。测试基于AMD EPYC 7763处理器。
问题
- 这一现象的原因是什么?为什么理论上完全在CPU内部实现的
rdrand指令会引发缓存访问? - 这些性能计数器的高数值是统计假象,还是意味着除了
rdrand自身延迟外,还有额外的性能损耗?
解答
1. 现象背后的原因
AMD EPYC 7763属于Zen 3架构,其RDRAND指令的实现并非完全脱离缓存子系统。实际上,Zen架构的随机数生成器(RNG)会将生成的随机数暂存到一个内部64字节对齐缓冲区中,但RDRAND指令读取数据的内部操作会被性能计数器识别为跨缓存行的未对齐加载(即ls_misal_loads.ma64统计的事件)。这是因为硬件内部的总线操作被性能计数器的事件定义覆盖,即使操作完全在CPU内部、未访问外部内存,也会被统计为缓存相关事件。
另外,all_data_cache_accesses统计的是所有数据缓存的访问请求,包括CPU内部硬件模块发起的请求,而非仅用户程序的内存访问。RNG模块为获取生成的随机数,会向L1缓存发起内部访问请求,这些请求自然会被计数器统计进去。
2. 计数器数值是否代表真实性能损耗
这些计数器数值并非统计假象,但额外性能损耗可忽略不计。
RDRAND指令的核心延迟主要来自随机数生成的硬件运算,而内部缓存访问的开销已经包含在指令的固有延迟中。也就是说,这些被统计的缓存访问和未对齐加载是RDRAND指令实现过程中必然存在的内部操作,不会因为用户程序的代码调整而减少。
你可以通过perf stat -e cycles统计总周期数,计算每次RDRAND的平均周期,这个数值应该和AMD官方文档给出的Zen 3架构RDRAND延迟(约17-20周期)基本一致,不会因这些统计到的缓存事件显著增加。
内容的提问来源于stack exchange,提问作者janw

