我一直在odroid-xu3上使用perf stat
和cpufreq-set
进行小型实验,这是一款带有Exynos芯片(A7和A15 ARM核的异构处理器)的嵌入式设备。我使用BLAS lvl3基准测试来运行我的实验,并且我一直使用taskset
实用程序将任务固定到A15核心。我还仔细检查了它是否是一个单线程实现。
希望在高频或低频下运行时循环次数应该相同,但我可以看到一个小的变化,例如分别在400MHz、1000MHz和1600MHz下运行GEMM内核(矩阵乘法,100次运行)。我获得了以下结果:
7166620830 cycles
17.923790714 seconds time elapsed
7235173436 cycles
7.237463382 seconds time elapsed
7428037080 cycles
4.643897351 seconds time elapsed
你可以看到,即使是持续时间也与频率不是真正的线性关系(这至少与测量的周期数一致…)。一种假设是,任务有点内存限制,但我对单精度实现有类似的结果。。。你知道这可能是什么原因吗?
编辑:矩阵有400个样本,我使用环境变量OPENBLAS_LOOP(OPENBLAS基准测试)运行它100次。我尽量避免其他应用程序运行,我看不出有0%的负载,但已经接近了。你建议我停止一些特别的事情吗?由于这已经是100多次实验的平均值,在同一频率下的变化非常低(<0.1%),当我改变频率时,大约有4%的差异,而且最高频率的周期数总是更大,所以看起来并不是更"嘈杂",而是在高频下发生了其他事情。
CPU周期不仅用于计算,还用于等待来自内存的数据。(是的,GEMM是BLAS3,在Roofline模型规模上具有非常好的算术强度和较低的内存读/写量,但仍有内存访问,并且它们的延迟与CPU频率不是线性的。)
尝试不仅检查CPU周期,还检查应该更稳定的指令计数器(如果这个性能计数器是为您的CPU实现的),还排除内核模式(它可能像调度器一样每100 Hz或300 Hz有一些周期性任务)使用:u
后缀:计数
perf stat -e cycles:u,instructions:u,task-clock:u ./program
(还要尝试查找为核心实现的一些缓存未命中事件或内存访问事件,也要检查核心文档中的原始编码,并使用已找到十六进制代码的-e rHHHH
)
当您更改CPU时钟频率时,您可能(也可能不会)也会影响内存控制器/内存总线频率(这特定于您的SoC和引导配置)。DRAM内存(可能是"exynos 5422"SoC中的LPDDR3)有许多定时是根据内存总线频率计算的,但实际上它们来自真实的内存数据库频率和延迟。
在绝对(ns)时序中,大多数时序都是相同的(或接近的),但有一个会影响代码周期:内存刷新时序-DRAM内存仅在短时间内保留数据(数据单元泄漏的电荷),类似于每32微秒(ms)或64毫秒进行一次完全刷新(这会随着高温而变化,通常有两个值-低温和高温)。有了完整的数据库刷新命令,它将在时间的某个部分无法访问,比如2%或5%(我没有确切的值)。
更改CPU频率时,不会更改刷新频率(数据应始终稳定,并根据内存芯片数据表的要求进行刷新)。但使用400 MHz CPU,计算时间更长,刷新次数也更多;并且对于1600MHz的计算是短的并且将看到更少的刷新。其他影响-一些内存请求可能会暂停,等待刷新结束。
因此,有一些非线性元素具有不同的贡献(对于低频周期,有些是负的,另一些是正的):
- CPU频率和DRAM刷新(绝对时间要求)显然不是线性效应
- CPU频率和Linux内核周期性任务(它们被安排为每秒运行几次,而不是每十亿个CPU周期运行一次)
- 实际数据访问DRAM时序(RAS,CAS)以ns为单位测量(但在存储器总线周期中建立和报告),并且1600MHz CPU可能具有100个周期从存储器获得第一个字节,但是400MHz CPU可能有60个周期从存储器获得第一个比特
最后一个效果看起来是你的结果中最显著的——低周期代表低MHz,高周期代表高MHz。在高频的情况下,CPU可能会停滞更多的周期,以等待来自存储器的几十ns来预充电/激活行/选择列。在低频率下,相同ns的内存延迟将转换为低数量的慢速CPU周期。