← SEARCHABLE ARCHIVE
C / C++ · 2026-08-15

从 C++ 基础到掌握 — 第 22 集

使用数据布局、缓存和 pmr 设计内存成本

INTERACTIVE READING MODE →

本博客文章以教学为目的编写。 内容可能存在错误,请结合目标编译器和标准库文档进行确认。

性能不只由运算次数决定。对象布局、访问顺序和分配频率会影响 CPU cache 与整体吞吐量。

连续数据更易利用 Cache

struct Particle
{
    float X, Y, Z;
    float VelocityX, VelocityY, VelocityZ;
    bool Active;
};

std::vector<Particle> particles;

当每次处理都使用大部分字段时,AoS 简单且局部性良好。但只更新位置时,未使用字段也可能被载入 cache line。

struct ParticlePositions
{
    std::vector<float> X, Y, Z;
};

SoA 让相同字段连续排列,有利于批处理与 SIMD,但会增加单个对象 API 的复杂度。

按生命周期设计分配

std::array<std::byte, 4096> buffer;
std::pmr::monotonic_buffer_resource arena(buffer.data(), buffer.size());
std::pmr::vector<std::pmr::string> names(&arena);

names.emplace_back("Knight");
names.emplace_back("Mage");

PMR 容器可在运行时注入 memory resource。monotonic_buffer_resource 只向前分配并一次释放全部内容,适合共享同一短生命周期的临时数据。

Resource 必须比所有使用它的容器和元素活得更久。arena 先销毁会让容器引用无效内存。

reserve 是重分配策略

std::vector<Entity> entities;
entities.reserve(expectedCount);

已知预计大小时,reserve 可减少反复分配和移动;估计过大则会长期占用未使用内存。重分配还会使地址和迭代器失效,长期外部引用可改用 index 或带 generation 的 handle。

不要在没有测量时改变 layout。用代表性 workload 比较 wall time、CPU time、分配次数、peak memory 和 cache miss,并重新验证正确性。

好的内存设计不是最聪明的 allocator,而是清楚表达数据生命周期和访问模式,并用最简单的所有权模型达到测量目标。