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

L1D缓存冲突测试:GCC与Clang中DoNotOptimize和volatile的行为差异及原因问询

L1D缓存冲突测试:GCC与Clang中DoNotOptimize和volatile的行为差异及原因问询

我现在在x86机器上用Google Benchmark测试L1数据缓存的冲突情况,遇到了一个困惑的问题,想请教大家:

测试代码

void bmRead(benchmark::State& state) {
    struct alignas(64) Line {
        char x[64];
        Line() {}
    };
    constexpr int sz = 1024;
    Line* lines = new Line[sz];
    int ind = 0;
    int steps = state.range(0);
    while (state.KeepRunning()) {
        for (int i = 0; i < 10; i++) {
            ind += steps;
            ind %= sz;
            /*volatile*/ char val = lines[ind].x[0];
            benchmark::DoNotOptimize(val);
            benchmark::ClobberMemory();
        }
    }
}
BENCHMARK(bmRead)->RangeMultiplier(2)->Range(1, 512);

预期行为

我的思路是触发L1缓存组冲突。这台CPU的L1D是VIPT结构(地址位[6–11]选择缓存组),8路组相联,所以每个缓存组最多只能同时容纳8个缓存行:

  • 当测试bmRead/64时,我们每隔64个缓存行访问一次→所有行都映射到同一个缓存组。工作集大小是1024/64=16个行,超过了8路的相联度,应该会触发缓存行驱逐,所以这个测试的耗时应该比其他情况明显更长。
  • 而bmRead/128的工作集只涉及8个缓存行(刚好能放进一个组),所以应该能正常高效执行。

测试结果

带volatile时的结果(符合预期)

首先是CPU缓存信息:

CPU Caches:
L1 Data 32 KiB (x8)
L1 Instruction 32 KiB (x8)
L2 Unified 512 KiB (x8)
L3 Unified 16384 KiB (x1)
Load Average: 0.29, 0.35, 0.19

基准测试结果:

-----------------------------------------------------
Benchmark               Time             CPU   Iterations
-----------------------------------------------------
bmRead/1             15.5 ns         15.5 ns     45195244
bmRead/2             15.5 ns         15.5 ns     45187565
bmRead/4             15.5 ns         15.5 ns     45156433
bmRead/8             15.5 ns         15.5 ns     45107741
bmRead/16            15.6 ns         15.5 ns     45115926
bmRead/32            15.5 ns         15.5 ns     45614777
bmRead/64            25.2 ns         25.2 ns     26902942
bmRead/128           18.3 ns         18.3 ns     38179996
bmRead/256           15.6 ns         15.6 ns     44976974
bmRead/512           15.5 ns         15.5 ns     45184535

可以看到bmRead/64的耗时明显更高,符合预期;bmRead/128表现正常。

去掉volatile仅依赖DoNotOptimize(val)的结果(不符合预期)

所有测试的耗时几乎完全一致,缓存冲突的影响消失了:

-----------------------------------------------------
Benchmark               Time             CPU   Iterations
-----------------------------------------------------
bmRead/1             14.2 ns         14.2 ns     48847532
bmRead/2             14.4 ns         14.4 ns     49097537
bmRead/4             14.4 ns         14.4 ns     48728445
bmRead/8             14.4 ns         14.4 ns     48684336
bmRead/16            14.6 ns         14.6 ns     48684349
bmRead/32            14.5 ns         14.5 ns     47601341
bmRead/64            14.4 ns         14.4 ns     48734250
bmRead/128           14.4 ns         14.4 ns     48585062
bmRead/256           14.4 ns         14.4 ns     48563453
bmRead/512           14.8 ns         14.8 ns     48398019

看起来加载操作被编译器完全优化掉了,我原本以为DoNotOptimize(val)能强制编译器保留这个加载操作,但实际并非如此。

反汇编分析

我查看了生成的汇编代码:

  • 用GCC编译且不带volatile时,循环里完全没有从lines[ind].x[0]加载数据的指令,相关片段如下:
0x55555555fa20 add %r12d,%ebx
0x55555555fa23 mov %ebx,%eax
0x55555555fa25 sar $0x1f,%eax
0x55555555fa28 shr $0x16,%eax
0x55555555fa2b add %eax,%ebx
0x55555555fa2d and $0x3ff,%ebx
0x55555555fa33 sub %eax,%ebx
0x55555555fa35 movslq %ebx,%rax
0x55555555fa38 shl $0x6,%rax
0x55555555fa3c dec %edx
0x55555555fa3e jne 0x55555555fa20
  • 而用Clang编译时,即使不带volatile,生成的机器码里也存在这个加载操作,测试结果也符合预期。

核心疑问

  1. 为什么GCC会把加载操作去掉?我以为DoNotOptimize(val)应该能强制编译器保留这个内存读取啊?
  2. 为什么Clang能正确保留加载并表现符合预期,而GCC不行?
  3. 是不是我对DoNotOptimize的理解有错误?

附加信息

  • 我试过在基准测试循环前给Line* lines = new Line[sz];填充随机值,但对测试结果没有影响。
  • 编译器版本:GCC 13.3.0,Clang 18.1.3
  • 编译命令:g++ -O3 -march=native -g file.cpp -lbenchmark -lpfm && ./a.out --benchmark_filter=Read
  • DoNotOptimize的实现代码:
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
#if defined(__clang__)
    asm volatile("" : "+r,m"(value) : : "memory");
#else
    asm volatile("" : "+m,r"(value) : : "memory");
#endif
}

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:48:07