You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

访问1-3个缓存行时PMU无L1缓存负载统计,CPU从何处取数?

L1缓存访问测试中负载计数异常的原因解析

现象回顾

测试访问不同数量的L1缓存行时,发现:

  • 访问1-3个缓存行:L1-dcache-loads事件计数极低,远低于理论循环次数
  • 访问4个及以上缓存行:L1-dcache-loads骤增,且随缓存行数量线性增长
  • 缓存行数量未超过L1容量时,miss率均<1%

该现象在AMD Zen2(Ryzen 5 PRO 4650G)和Intel Skylake-SP(Xeon Silver 4116)上均复现,两者L1缓存均为32KB、64字节缓存行。

核心原因:写回缓冲区的容量限制与负载计数规则

这个现象和CPU的**写回缓冲区(Write Buffer)**直接相关,而非AGU单元数量:

  1. 读-修改-写操作的计数逻辑
    测试代码中的buffer[i_line][0] +=1属于读-修改-写操作:先从内存/缓存读取数据到寄存器,修改后写回。L1-dcache-loads统计的是从L1缓存加载数据到寄存器的事件,但如果数据能在写回缓冲区(或存储队列)中命中,读操作会直接从这些内部缓冲区获取,不会触发L1加载计数。

  2. 写回缓冲区的容量阈值
    x86架构的CPU写回缓冲区通常包含3-4个条目(Zen2和Skylake-SP的写回缓冲区正好是3-4个条目):

    • 当访问1-3个缓存行时,每个缓存行的写操作对应的条目可以长期驻留在写回缓冲区中,后续的读-修改-写操作直接命中缓冲区,无需从L1缓存加载,因此L1-dcache-loads计数极低。
    • 当访问4个缓存行时,写回缓冲区被占满,旧的条目会被刷入L1缓存,后续的读操作必须从L1缓存加载数据,因此L1-dcache-loads计数骤增,且与循环次数(repeat * N_LINES)基本匹配。
  3. 与L1组相联无关的原因
    你原本预期8个缓存行(对应AMD L1的8路组相联)会有性能变化,但组相联影响的是缓存冲突miss,而当前现象是写操作的内部缓冲区旁路机制导致的,和缓存映射规则无关,因此8个行时不会出现特殊变化。

验证数据匹配

从你提供的统计数据可以直接验证:

  • 3个缓存行时:L1-dcache-loads仅34,374,远低于理论值(1000*128*2*16*16 *3 = 196,608,000),说明绝大多数读操作命中了写回缓冲区。
  • 4个缓存行时:L1-dcache-loads为260,407,342,接近理论值(1000*128*2*16*16 *4 = 262,144,000),说明每个读操作都触发了L1缓存加载。

额外验证方法

如果想进一步确认结论,可以修改测试代码为单纯的读操作:

uint64_t tmp = buffer[i_line][0];

此时不管访问1-3还是4个缓存行,L1-dcache-loads都会接近理论循环次数——因为读操作直接从L1缓存加载(预热后),不会经过写回缓冲区的旁路。


测试相关代码与命令

测试程序

#define N_LINES 1
#define CACHE_LINE_SIZE 64

alignas(CACHE_LINE_SIZE) uint64_t buffer [N_LINES][CACHE_LINE_SIZE / sizeof(uint64_t)];

static long long unsigned repeat = 1000* 128 * 2 * 16 * 16 ;

int main(int argc, char* argv[])
{
    // pin CPU
    // warmup data, i.e. loop & write in buffer[i][0] = i

    for (unsigned rep=0; rep<repeat; rep++) {
        for (unsigned i_line=0; i_line<N_LINES; i_line++) {
            // touch the first uint64 in the line
            buffer[i_line][0] += 1;
        }
    }
}

(注:原代码中i_block为笔误,修正为i_line)

编译与perf命令

g++ -g -O1 ./test_cache.cpp
sudo perf stat \
  -e task-clock,instructions,cycles,stalled-cycles-frontend,stalled-cycles-backend \
  -e L1-dcache-loads,L1-dcache-load-misses,L1-dcache-prefetches \
  -- ./a.out

统计数据示例

# 3个缓存行时
            34,374      L1-dcache-loads                  #    1.006 M/sec                       (35.11%)
             2,460      L1-dcache-load-misses            #    7.16% of all L1-dcache accesses   (35.11%)

# 4个缓存行时
       260,407,342      L1-dcache-loads                  #    1.938 G/sec                       (17.86%)
             5,037      L1-dcache-load-misses            #    0.00% of all L1-dcache accesses   (17.86%)

内容的提问来源于stack exchange,提问作者xealits

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:28:20