SIMD、多核与缓存一致性¶
为什么需要多种并行¶
单条指令内部、连续数据元素之间、独立线程之间都有潜在并行。不同层次需要不同硬件:
- 指令级并行让多条无关指令重叠;
- SIMD/向量让一条指令处理多个数据元素;
- 多核让多个线程真正同时执行;
- 多机并行再把通信扩展到网络。
并行不是复制运算单元就结束,还必须供给数据、划分任务并协调共享状态。
SIMD 与向量计算¶
标量加法一次处理一对数,SIMD 指令可同时处理若干 lane:
理想情况下,规则数组运算能以较少指令获得高吞吐。限制包括:
- 数据长度与对齐不合适,需要处理尾部;
- 条件分支造成 lane 掩码和利用率下降;
- gather/scatter 不连续访存昂贵;
- 内存带宽可能先于算力饱和。
向量 ISA 让向量长度更抽象,硬件可按实际 lane 数分批执行,软件兼容性更好。
多核的共享内存幻觉¶
每个核心通常有私有 L1/L2 cache,却共享更低层 cache 或主存。若核心 A 修改变量 x,核心 B 的私有 cache 可能仍保存旧副本。没有协议,“共享内存”就不能维持单一值的直觉。
cache coherence¶
一致性协议保证对同一 cache 行的写最终按规则被其他核心观察。常见写失效协议在一个核心获得可写权限时,使其他核心副本失效。
MESI 用四类状态描述 cache 行:
- Modified:本核独占且已修改,下层可能旧;
- Exclusive:本核独占但干净;
- Shared:可能有多个干净副本;
- Invalid:本核副本不可用。
核心写 Shared 行前要请求所有权并让其他副本转为 Invalid。总线嗅探适合核心数较少的共享互连;目录协议记录共享者,扩展性更好但需要目录存储和消息流程。
false sharing¶
两个线程修改不同变量,若变量恰在同一 cache 行,一致性仍以整行为单位反复转移所有权:
程序语义没有共享同一变量,性能上却像激烈竞争。通过填充、分片或改变数据布局可缓解,但会增加内存占用。
coherence 不等于 consistency¶
coherence 主要回答同一地址的写如何传播;memory consistency 回答不同地址的 load/store 在其他核心看来允许怎样排序。
处理器和编译器会重排独立访存以提高性能。同步算法需要原子指令与内存屏障建立顺序。例如“先写数据,再发布 ready 标志”必须保证另一个核心看到 ready 后也能看到数据。
更强顺序模型易编程,却限制优化;较弱模型允许更高性能,但软件必须明确同步。语言内存模型、编译器和 ISA 三层都要一致配合。
原子操作¶
原子读改写保证观察和更新不可被其他核心插入,常见有 compare-and-swap、fetch-add 或 load-reserved/store-conditional。它们是锁和无锁结构的基础,但高竞争原子变量会让 cache 行频繁迁移,成为串行瓶颈。
最小并行例子¶
把长度为 \(N\) 的数组求和分给 \(P\) 个核心。每核计算局部和,最后归约。理想计算量约降为 \(N/P\),但还需分配任务、读取内存、同步和合并结果。若每核都频繁更新同一个全局和,原子竞争会抵消并行收益;局部累加后一次合并通常更好。
历史与改进空间¶
从提高频率转向多核后,一致性流量和内存带宽成为关键约束。更多核心不只增加算力,也扩大通信距离和协议状态。分层目录、非一致内存访问(NUMA)、消息传递和领域专用互连都是不同折中。
自测¶
- 什么工作负载适合 SIMD,什么控制模式会降低 lane 利用率?
- false sharing 为什么不影响程序逻辑正确性,却能严重拖慢性能?
- coherence 与 consistency 的问题边界分别是什么?