arm中内存读取延迟&&性能测试
引言
在现代计算中,内存访问延迟是影响整体系统性能的关键因素之一。尤其是在嵌入式系统和移动设备中,ARM架构凭借其低功耗特性广泛应用,但内存读取延迟的优化仍然是开发者面临的重要挑战之一。通过对ARM系统中内存读取延迟的性能测试,可以更好地理解内存访问模式、缓存层次结构和处理器架构对系统性能的影响,进而优化软件和硬件设计。
什么是内存读取延迟?
内存读取延迟是指从处理器发出内存读取请求到数据真正返回的时间间隔。 内存读取延迟的高低受多种因素影响,包括CPU缓存层次(如L1、L2、L3缓存)、内存带宽、内存类型(如DRAM、SRAM)、系统架构以及访问模式等。
内存延迟通常分为以下几个层级:
- L1缓存延迟 :这是最接近处理器核心的缓存,访问速度最快。
- L2缓存延迟 :较大的二级缓存,速度次于L1缓存。
- 主内存延迟 :如果数据不在缓存中,处理器需要从主内存(DRAM)加载数据,延迟较大。
ARM架构与内存读取延迟
ARM架构的设计注重低功耗与高效性,内存访问延迟的优化尤为重要。ARM处理器采用了多级缓存结构(L1、L2、L3缓存),并通过流水线技术和乱序执行来提高执行效率。然而,内存读取延迟在ARM架构中的表现依然受到以下因素影响:
- 缓存层次结构 :ARM处理器通常具有多级缓存,L1缓存是最快的,但其容量较小;L2缓存较大,但访问速度较慢;如果数据不在缓存中,ARM处理器将需要访问更慢的主内存。
- 内存带宽和访问模式 :内存访问模式(如顺序访问、随机访问等)对延迟有显著影响。顺序访问通常比随机访问具有更低的延迟。
- 处理器频率与时钟 :ARM处理器的工作频率与内存延迟密切相关。较高的频率有助于降低延迟,但也可能导致更高的功耗。
内存读取延迟性能测试
进行内存读取延迟性能测试时,目标是分析不同类型的内存访问(缓存命中与未命中)在不同层次的缓存(L1、L2、主内存)中的表现。以下是进行内存读取延迟性能测试的一些基本方法和技巧:
1. 缓存命中与未命中测试
测试缓存命中与未命中的延迟差异是评估内存访问性能的核心。测试可以分为以下几种场景:
- L1缓存命中 :当数据已经被加载到L1缓存中时,读取延迟最短。
- L2缓存命中 :数据不在L1缓存中,但在L2缓存中时,访问延迟较长。
- 主内存访问 :当数据既不在L1缓存也不在L2缓存中时,处理器需要从主内存读取数据,这时的延迟是最大的。
为了测试缓存命中与未命中,可以使用大规模数组访问,测量数据访问时间,并使用不同的访问模式来调整数据在缓存中的位置。
2. 内存带宽测试
内存带宽直接影响数据的读取速率,进而影响延迟。通过测量单位时间内能够读取的数据量,可以得出内存带宽并进一步分析其对延迟的影响。常用的内存带宽测试方法包括:
- 单一内存读取 :测试单个内存读取操作的延迟。
- 批量内存读取 :测试大规模数据读取的延迟。
- 并行内存访问 :测试多个线程或核心并发访问内存时的延迟。
3. 访问模式测试
不同的访问模式会影响内存延迟。常见的测试访问模式包括:
- 顺序访问 :以连续的内存地址访问数据,通常会引起较少的缓存未命中。
- 随机访问 :随机读取内存中的数据,可能会导致较多的缓存未命中。
- 跳跃访问 :访问数组中的数据元素,间隔一定数量的内存地址,测试缓存的响应能力。
4. ARM性能分析工具
ARM提供了一些专业的工具来帮助开发者分析内存读取延迟:
- ARM DS-5 :ARM的开发套件,包括性能分析工具,用于检测内存访问模式、缓存命中率等。
- ARM Streamline :通过收集硬件事件来分析系统性能,包括内存访问延迟、缓存使用情况等。
- perf工具 :Linux下的性能分析工具,可以用来测量CPU周期、缓存命中率和内存访问延迟。
5. ARM性能分析工具
- arm对于预取监控的说明 * 说明文档中对cache中的指令预取有关于该监控的说明,但是是可选实现的, orin的cpu没有实现
- cache miss
- armv8中的缓存未命中中明确说明了不包含等待预取
- 现在我们通过stall_backend, stall_backend_mem来反应等待预取指令造成的等内存造成的延迟情况

在该程序中,我们进行循环来模拟控制频繁的触发cpu计算指令和预取指令的情况,来统计我们需要统计的指标. 其中我们用register来操作经常需要操作的单元,避免频繁的内存读取变量指令.
通过更改step,我们可以控制计算密度和预取指令密度的占比,来观察不同的计算密度下,等待预取指令会造成多少延迟,通过 stall_backend_mem, stall_backend指标来判断由于等内存造成的延迟
以下分别是step取 1 10 20的对比结果, 使用命令 perf stat -e cpu_cycles,stall_backend,stall_backend_mem,l1d_cache,l1d_cache_lmiss_rd,l1d_cache_refill ./main

可以看到随着step的变大,stall的占总cpu-cycles的比例增大很多,在step < 12 左右的时候,会明显增长. 说明在计算密集的情况下,cpu的耗时时长大于 > 预取的耗时。
| step | cpu_cycles | stall_backend | stall_backend_mem | l1d_cache_lmiss_rd | L1d_cache | L1d_cache_miss | cost_time |
|---|---|---|---|---|---|---|---|
| 1 | 10,774,068,256 | 15,961,865 | 4,481,713 | 1,815,144 | 10,740,266,111 | 2,649,200 | 4.923 |
| 3 | armv8_pmuv3/cpu_cycles/ | 8,844,137 | 3,568,493 | 1,239,874 | 3,578,921,544 | 1,266,233 | 1.646 |
| 5 | 2,170,918,674 | 26,081,967 | 3,807,245 | 1,459,202 | 2,148,558,568 | 25,269,324 | 0.993 |
| 10 | 1,380,895,447 | 496,599,435 | 4,160,807 | 142,925,792 | 1,074,332,576 | 129,840,054 | 0.631 |
| 12 | 1,328,605,278 | 616,951,225 | 3,221,791 | 140,491,881 | 894,689,829 | 142,118,219 | 0.609 |
| 14 | 1,308,598,310 | 701,042,337 | 2,458,772 | 146,820,568 | 768,922,927 | 146,498,022 | 0.599 |
| 15 | 1,298,169,873 | 725,554,152 | 2,136,513 | 147,260,156 | 717,849,671 | 146,271,280 | 0.595 |
| 20 | 1,258,118,566 | 811,264,455 | 4,066,244 | 147,689,763 | 538,240,400 | 146,564,694 | 0.577 |
| 25 | 1,243,297,210 | 875,309,426 | 5,518,142 | 147,570,611 | 431,449,168 | 146,796,595 | 0.568 |
| 64 | 1,215,813,143 | 1,037,781,709 | 2,661,534 | 147,480,144 | 170,240,690 | 146,433,145 | 0.557 |
以上是data = 32k, step = 20左右会有明显的内存延迟,stall_mem / cpu_cycles = 千分之3 stall_mem / stall_backend = 千分之5,stall_backend / cpu_cycles = 64%
l1d_cache_lmiss_rd → 10% - 13%会有明显的内存读取延迟
| step | cpu_cycles | stall_backend | stall_backend_mem | l1d_cache_lmiss_rd | l1d_cache | l1d_cache_miss | instractions | cost_time |
|---|---|---|---|---|---|---|---|---|
| 1 | 10,762,950,137 | 13,565,269 | 10,320,381 | 324,453 | 10,743,196,255 | 352,468 | 53,732,664,980 | 4.916 |
| 3 | 3,593,909,213 | 6,825,819 | 5,510,889 | 133,637 | 3,579,581,280 | 159,325 | 17,944,302,605 | 1.645 |
| 5 | 2,160,344,451 | 7,619,354 | 6,171,211 | 157,456 | 2,148,123,091 | 251,658 | 10,787,790,141 | 0.991 |
| 10 | 1,109,773,912 | 42,670,109 | 5,633,914 | 10,253,700 | 1,073,989,794 | 10,240,002 | 5,425,744,718 | 0.509 |
| 12 | 936,406,114 | 53,668,535 | 8,400,680 | 10,686,951 | 893,804,300 | 10,690,769 | 4,527,029,795 | 0.430 |
| 13 | 872,917,364 | 55,542,299 | 7,782,462 | 10,781,588 | 826,732,159 | 10,841,537 | 0.401 | |
| 14 | 817,947,391 | 62,626,455 | 7,582,563 | 12,037,911 | 767,439,457 | 20,999,380 | 0.376 | |
| 15 | 823,199,284 | 151,055,872 | 13,823,014 | 112,617,289 | 716,312,483 | 104,852,559 | 0.378 | |
| 16 | 821,392,615 | 357,704,653 | 9,727,012 | 129,473,297 | 596,901,687 | 158,385,068 | 0.376 | |
| 20 | 813,227,142 | 400,572,615 | 22,831,501 | 162,718,604 | 537,200,691 | 161,809,725 | 2,720,149,200 | 0.373 |
| 25 | 856,822,525 | 500,039,324 | 242,639,648 | 160,197,029 | 430,988,828 | 157,921,394 | 0.393 | |
| 64 | 816,391,898 | 677,940,144 | 543,703,701 | 167,798,278 | 169,411,861 | 166,365,196 | 0.375 | |
以上数据是32M数据的情况。mem占比总cpu_cycle百分之一左右就会有明显的内存读取延迟(step=14 data=32M), 其中 stall_mem / stall_backend = 12%, stall_backend / cpu_cycles=7.6%
armv8_pmuv3/cpu_cycles/,armv8_pmuv3/inst_retired/,armv8_pmuv3/l1d_cache/,armv8_pmuv3/l1d_cache_lmiss_rd/,armv8_pmuv3/l1i_cache/,armv8_pmuv3/l1i_cache_lmiss/,armv8_pmuv3/stall_backend/,armv8_pmuv3/stall_backend_mem/,
实车测试 23095清扫车orin
32M情况下,step = 14之后会有固定的时间耗时,且此时 backend / cycle = 20%, backend_mem / cycle = 10%
测试结果如下
| step | cycle | stall_backend | stall_backend_mem | l1d_cache_lmiss_rd | l1d_cache | l1dcache_miss | cost_time | |
|---|---|---|---|---|---|---|---|---|
| 1 | 11,359,661,318 | 2,465,950,612 | 279,040,830 | 1,913,863 | 111,067,784 | 1,928,783 | 5.262 | |
| 3 | 3,771,645,781 | 713,916,862 | 79,813,652 | 608,985 | 34,145,863 | 610,586 | 1.757 | |
| 5 | 2,228,215,722 | 357,487,517 | 28,562,545 | 260,559 | 14,591,457 | 260,085 | 1.074 | |
| 8 | 1,359,458,589 | 132,129,621 | 3,683,699 | 80,490 | 4,230,744 | 80,229 | 0.636 | |
| 10 | 1,088,056,939 | 4,436,963 | 3,335,563 | 63,569 | 1,076,440,192 | 63,712 | 0.538 | |
| 12 | 907,659,992 | 3,897,485 | 2,501,085 | 57,558 | 896,886,681 | 58,166 | 0.429 | |
| 13 | 884,107,663 | 29,707,950 | 24,099,854 | 177,287 | 835,435,776 | 198,287 | 0.412 | |
| 14 | 782,781,913 | 4,981,387 | 3,736,565 | 59,447 | 769,648,290 | 59,447 | 0.353 | |
| 15 | 727,105,990 | 3,735,820 | 2,697,526 | 51,072 | 718,050,179 | 47,883 | 0.347 | |
| 16 | 685,992,596 | 4,841,683 | 3,569,864 | 60,066 | 673,568,933 | 59,499 | 0.332 | |
| 17 | 645,771,179 | 5,269,752 | 4,176,450 | 50,713 | 633,315,262 | 53,063 | 0.310 | |
| 20 | ||||||||
| 23 | ||||||||
| 24 | ||||||||
| 25 | ||||||||
| 30 | ||||||||
| 64 |
性能测试的优化建议
- 优化数据布局与对齐 :确保数据结构在内存中是连续对齐的,可以减少内存访问时的延迟。
- 减小缓存冲突 :通过调整数据访问模式和结构布局,避免多个线程或核心争用相同的缓存行。
- 合理利用缓存 :设计时尽量优化数据访问模式,以提高缓存命中率,减少内存访问延迟。
- 提高内存带宽利用率 :通过并行化内存操作、优化内存访问等方法,减少内存带宽瓶颈的影响。
结论
ARM架构在内存访问方面具有独特的优势和挑战。通过针对不同缓存层次的性能测试、优化内存访问模式、减少缓存未命中等方法,可以显著提高ARM系统中的内存读取性能。随着ARM架构在移动设备和嵌入式领域的广泛应用,理解内存延迟的影响并采取有效的性能优化策略,将是开发高效能应用和系统的关键。
