缓存层级
L1: 32KB, ~1ns, 每核独享
L2: 256KB, ~4ns, 每核独享
L3: 8-32MB, ~12ns, 核共享
内存: 16GB+, ~100ns
缓存行 (cache line): 64 字节,原子单位
读取 1 字节 → 实际加载整行 64 字节
伪共享 (False Sharing)
// ❌ 两个线程各自修改相邻变量 → 同一缓存行 → 互相失效
struct BadLayout {
std::atomic<int> counter1; // 偏移 0
std::atomic<int> counter2; // 偏移 4 ← 同一缓存行!
};
// counter1++ 和 counter2++ 会互相使对方的缓存行无效
// → 性能下降 10-100 倍
// ✅ 对齐到不同缓存行
struct alignas(64) GoodLayout {
std::atomic<int> counter1;
char pad1[60]; // 填充到 64 字节
};
struct alignas(64) GoodLayout2 {
std::atomic<int> counter2;
char pad2[60];
};
数据布局
// ❌ AoS (Array of Structs) — 遍历一个字段时拖入整行无关数据
struct Particle { float x, y, z, vx, vy, vz, mass; };
Particle particles[1000000];
// 遍历所有 x — 每次读 64 字节 = 2.3 个 Particle
// → 大量带宽浪费在不需要的 y, z, vx, vy 上
// ✅ SoA (Struct of Arrays) — 每个字段连续存储
struct Particles {
std::vector<float> x, y, z, vx, vy, vz, mass;
};
// 遍历所有 x — 每次读 64 字节 = 16 个 float
// → 带宽利用率高 7 倍
分支预测与 Spectre
// 对性能敏感的循环,排序数据可以加速
std::sort(data.begin(), data.end()); // 排序后分支可预测
for (auto x : data) {
if (x > threshold) // 连续的真/假 → 分支预测 100% 命中
doA();
else
doB();
}
// 不排序: 分支预测 ~50% 命中 → 流水线刷新 → 慢 3-5 倍
prefetch
#include <xmmintrin.h>
// 提前告知 CPU 将要访问的地址 → 后台加载
for (int i = 0; i < N; ++i) {
_mm_prefetch((char *)&data[i + 16], _MM_HINT_T0); // 提前 16 步
process(data[i]);
}
// 适用于链表遍历等随机访问模式