Fork me on GitHub
侧边栏

【ARM Cache 及 MMU 系列文章 6.5 -- 如何进行 Cache miss 统计?】

ARM Cache Miss 统计

在ARMv8/v9架构中,缓存未命中(Cache Miss)的统计对于性能调优和系统分析至关重要。缓存未命中意味着处理器尝试从缓存中读取数据时没有找到,因此不得不从更低速的存储(如L2缓存或主内存)中加载数据,这会导致延迟增加和性能下降。理解和分析缓存未命中有助于发现程序的瓶颈,从而做出相应的优化。

Cache 多层架构简介

ARMv8/v9 架构通常包含多级缓存,最常见的是三级缓存:

  • L1缓存 :分为指令缓存(I-cache)和数据缓存(D-cache),通常每个核心都有自己的L1缓存。
  • L2缓存 :作为一个桥梁,连接快速但容量较小的L1缓存和慢速但容量较大的L3或主内存。L2缓存可能对每个处理器核心是独立的,也可能是几个核心共享的。
  • L3缓存 :在多核处理器上,L3缓存通常是所有核心共享的,容量更大,但速度慢于L1和L2缓存。

Cache 未命中的类型

  • 指令缓存未命中 :当处理器试图从指令缓存中读取执行指令而未命中时发生。这会导致处理器从L2缓存或更低级别的存储中加载指令。
  • 数据缓存未命中 :当处理器试图从数据缓存中读取或写入数据而未命中时发生。这迫使处理器从更低一级的缓存或主内存中加载数据。

Cache 未命中统计

在ARMv8/v9架构中,可以通过多种方式统计缓存未命中,这通常涉及到性能监视单元(Performance Monitoring Unit, PMU)的使用。PMU是ARM处理器的一个组件,可以统计包括缓存未命中在内的多种性能事件。

  1. 使用PMU寄存器 :ARMv8/v9提供了专门的寄存器来配置和读取PMU事件计数器。通过编程这些寄存器,可以选择需要监控的特定事件(例如L1数据缓存未命中、L2缓存未命中等)并读取事件计数。
  2. 软件工具 :一些软件工具,如 perf (Linux性能分析工具)也可以用来统计缓存未命中。这些工具内部使用PMU或其他机制来收集数据,为用户提供了一个更为简洁和易用的接口。

本文将会以直接配置PMU 寄存器的方式来统计cache miss 数据。

Cache miss 统计代码实现

本文以统计L1 Data Cache 的miss 为例进行介绍。由于测试环境是在EL3先进行,所以这里需要先配置 mdcr_el3 , 使能 EL3异常等级下 PMU 统计的功能。

  .section .text, "ax", %progbits

.equ NUM_ITERATIONS, 20
.equ PMU_COUNTER0,               0x0
.equ PMU_EVENT_L1D_CACHE_REFILL, 0x3
.equ TEST_PASS,                  0x1
.equ TEST_FAIL,                  0x0

.global l1_cache_miss
.type   l1_cache_miss, %function

l1_cache_miss:
    stp     x29, x30, [sp, #-16]!

    ldr     x9, =NUM_ITERATIONS

    /* Allow PMU counting at EL3 */
    mrs     x0, mdcr_el3
    orr     x0, x0, #(1 << 17)
    msr     mdcr_el3, x0

    /* Clear all counters */
    mrs     x0, pmcr_el0
    bic     x0, x0, #0xF
    msr     pmcr_el0, x0
    isb

    /* Set up counter for D cache miss */
    mov     x1, #PMU_COUNTER0
    msr     pmselr_el0, x1
    mov     x1, #PMU_EVENT_L1D_CACHE_REFILL
    msr     pmxevtyper_el0, x1

    /* Clear overflow status and enable */
    ldr     x1, =0x80000001
    msr     pmovsclr_el0, x1
    msr     pmcntenset_el0, x1

    /* Reset and enable counters */
    orr     x0, x0, #0x7          
    msr     pmcr_el0, x0
    isb

loop:           adr     x0, data_array1
                adr     x1, data_array2
                adr     x2, data_array3

                /* Clean and invalidate */
                dsb     sy
                dc      CIVAC, x0
                dc      CIVAC, x1
                dc      CIVAC, x2
                dsb     sy

                ldr     x1, [x0]
                ldr     x2, [x1]
                ldr     x3, [x2]
                isb

                subs    x9, x9, #0x1
                bne     loop

                dsb     sy
                isb

                /* Disable counters */
                mrs     x0, pmcr_el0
                bic     x0, x0, #0x1
                msr     pmcr_el0, x0

                /* Read event counter into x0*/
                mov     x0, #PMU_COUNTER0
                msr     pmselr_el0, x0
                mrs     x0, pmxevcntr_el0

                ldr     x1, =NUM_ITERATIONS
                mov     x2, #0x3
                madd    x2, x2, x1, xzr
                cmp     x0, x2

                mov     x0, #TEST_PASS
                b.pl    test_finish
                mov     x0, #TEST_FAIL
test_finish:
                bl      test_result_output
                ldp     x29,x30, [sp], #16
                ret


    .section .data, "aw", %progbits
data_array1:     .quad data_array2
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A
                .word 0x5A5A5A5A

data_array2:     .quad data_array3
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000
                .word 0xFFFFFFFF
                .word 0x00000000

data_array3:     .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF
                .word 0xDEADBEEF

上面测试代码可以分为三个部分:

  • 配置系统寄存器,允许 EL3 异常登记下PMU 对 cache miss的统计;
  • 配置PMU寄存器来进行 L1 Data Cache Miss 事件的统计测试;
  • 数据访问及cache操作。

了解缓存未命中的情况后,可以通过各种优化策略减少缓存未命中的发生:

  • 提高数据局部性 :通过优化数据结构和访问模式,尽量保证数据访问集中在相邻的内存位置。
  • 循环展开 :减少循环中的迭代次数,以减少指令缓存未命中的机会。
  • 预取指令和数据 :在数据或指令被需要之前就加载它们到缓存中。

Cache Miss 统计意义

缓存未命中统计是理解和优化ARMv8架构性能的关键环节。通过监控和分析缓存未命中,开发者可以识别性能瓶颈并采取措施进行优化,从而提高应用程序的整体性能。

posted @ 2025-09-13 10:00  yooooooo  阅读(31)  评论(0)    收藏  举报