基础能力系列 - Cache-Friendly 编程技巧

在现代 C++ 编程中,cache-friendly(缓存友好) 是一种优化思维方式,旨在最大程度地利用 CPU 的高速缓存(L1/L2/L3 Cache),提高内存访问效率,降低 cache miss 和访问延迟,从而显著提升程序性能。

一、缓存友好编程的核心理念

  • CPU cache 是按 cache line(通常是 64 字节)为单位加载数据的。
  • 顺序访问内存 会预取下一行,提升命中率。
  • 非顺序(跳跃)访问、频繁分配小对象、链式结构 会导致 cache miss。

二、实用的 cache-friendly 编程技巧

1. 结构体/数组数据排列成"结构体数组(SoA)"

不推荐(AoS:Array of Structs):
cpp 复制代码
struct Particle {
    float x, y, z;
    float velocity;
};

std::vector<Particle> particles;
  • 遍历只 x/y/z 会带入很多无用数据(velocity)。
  • 空间局部性差。
推荐(SoA:Struct of Arrays):
cpp 复制代码
struct ParticleSystem {
    std::vector<float> x, y, z, velocity;
};
  • 顺序访问一个字段变得高效,能充分利用 cache line。
  • 有利于 SIMD 向量化优化。

2. 尽量使用连续内存结构(如 std::vector

相比于:

cpp 复制代码
std::list<int> list;

更推荐使用:

cpp 复制代码
std::vector<int> vec;
  • std::list 分配离散内存块,cache 友好性差。
  • std::vector 分配一整块内存,访问是顺序且预取友好。

3. 避免频繁分配/释放内存(避免堆分配)

  • 分配小对象(如大量 new T)容易污染 cache 和 TLB。
  • 尽量使用 内存池(memory pool)对象池(object pool),复用对象。
  • 在多线程环境下,可以使用 thread-local allocator 避免 false sharing。

4. 循环优化:使用循环展开 + 预取优化

cpp 复制代码
for (size_t i = 0; i < N; ++i) {
    a[i] += b[i];
}

改进:

  • 使用 循环展开(loop unrolling)
  • 手动预取下一块数据(有些编译器会自动做)。

5. 按访问顺序布局结构体字段

cpp 复制代码
struct Foo {
    char a;       // 1 byte
    double b;     // 8 bytes
    int c;        // 4 bytes
};

改成:

cpp 复制代码
struct Foo {
    double b;     // 8 bytes
    int c;        // 4 bytes
    char a;       // 1 byte
};
  • 避免 padding,节省空间。
  • 更重要:连续访问字段时能落在同一个 cache line 内。

6. 防止 false sharing(伪共享)

多个线程访问不同变量时,如果变量落在同一个 cache line,会导致性能急剧下降。

解决方法:

cpp 复制代码
struct alignas(64) Counter {
    int value;
    char padding[60]; // 手动填充
};

或使用:

cpp 复制代码
#include <atomic>
#include <new>
alignas(64) std::atomic<int> counter;

7. 使用 SIMD 和预取指令(高级优化)

如 Intel 的 SSE/AVX、ARM 的 NEON:

cpp 复制代码
#include <immintrin.h>  // AVX
__m256 vecA = _mm256_loadu_ps(&a[i]);
__m256 vecB = _mm256_loadu_ps(&b[i]);
__m256 vecC = _mm256_add_ps(vecA, vecB);
_mm256_storeu_ps(&c[i], vecC);

配合 cache-friendly 数据布局,SIMD 能极大加速批量处理任务。


三、小结:缓存友好技巧汇总

技巧 描述
使用 std::vector 替代链表 保证内存连续性
使用 SoA 替代 AoS 避免加载无关数据
结构体字段按访问顺序排列 减少 cache miss
避免频繁小对象堆分配 使用对象池
防止 false sharing 跨线程数据加 padding
合理使用预取和 SIMD 配合数据布局提升性能

: 基础能力系列
: 区块链知识系列
: 密码学系列
: 零知识证明系列
: 共识系列
: 公链调研系列
: BTC系列
: 以太坊系列
: EOS系列
: Filecoin系列
: 联盟链系列
: Fabric系列
: 智能合约系列
: Token系列

相关推荐
搬砖魁首9 天前
基础能力系列 - CPU和GPU的Cache Line
缓存行·cache line·结构体对齐·false sharing·内存访问模式·内存填充
alwaysrun11 天前
Zig之标量、向量与SIMD
vector·向量·simd·zig
十五年专注C++开发4 个月前
浅谈CPU中的SIMD
c++·cpu·代码优化·simd
橘色的喵4 个月前
嵌入式 ARM Linux 平台高性能无锁异步日志系统设计与实现
linux·arm开发·cache line·ring buffer
赖small强5 个月前
【Linux 内存管理】深入解析Linux缓存行(Cache Line)与内存不对齐引发的Bus Error硬件异常
内存对齐·linux内存管理·cache line·bus error
赖small强5 个月前
【Linux 内存管理】深入解析 Linux Cache Line 的原理、价值及 MIPS CPU 处理机制
linux·缓存·内存对齐·cache line
高铭杰7 个月前
Postgresql源码(149)SIMD应用与性能测试
数据库·postgresql·sse·simd
ChipCamp7 个月前
GPU微架构演进分析 -- SIMT微架构 vs SIMD微架构
simd·gpu微架构·simt
深度混淆10 个月前
C#,List<T> 与 Vector<T>
开发语言·c#·vector·list·simd