一句话:内存对齐(memory alignment)管的是"每个数据从哪个地址开始放",数据布局(data layout)管的是"一堆数据按什么顺序挤在一起";AoS 与 SoA 就是后者的两种极端排法。
为什么会有"对齐"这回事
CPU 不是逐字节读内存的,而是按块搬。常见做法是以 64 字节为一个缓存行(cache line),你只要碰到其中一个字节,整行 64 字节都会被搬进缓存。如果一个 4 字节整数横跨两行,就得访问两次;在某些架构上,未对齐的访问甚至直接出错。于是编译器和运行时会做对齐:让每个字段的起始地址落在它大小的倍数上,必要时插入空白(padding)。对齐是为了"能读",布局是为了"读得值"。
打个体检的比方。AoS(Array of Structures,结构体数组)像每个人做完所有项目再换下一位:姓名、身高、体重、血压都记在这个人的档案里。SoA(Structure of Arrays,数组的结构体)像所有人体重排一队、所有身高排一队。如果医生只想统计"所有人的体重",SoA 的队伍是一条顺序扫描,AoS 却要在每份档案里跳过三个无关字段——数据总量一样,有效带宽差好几倍。
两种布局的取舍
| 维度 | AoS | SoA |
|---|---|---|
| 内存形态 | 一个对象的所有字段相邻 | 同一字段全局连续 |
| 适合场景 | 需要同时读写同一对象的多个字段 | 批量处理单个字段 |
| SIMD 友好度 | 差,字段交错需重排 | 好,天然可一次算一批 |
| 缓存利用 | 单对象局部性好,批量时浪费 | 批量时几乎不浪费 |
注意:AoS 并不总是差。做粒子模拟,一个粒子的位置和速度要一起更新,AoS 反而省事;一旦变成"只更新所有粒子的位置",SoA 就赢了。
对 AI 从业者的实际意义
这不是底层程序员的专利,它每天都在深度学习里发生:
- AI 词典:Embedding">Embedding 查表:取一条向量时,若它和一堆无关字段绑在一起,缓存里塞满用不上的字节,吞吐掉得很快。
- 点积与矩阵乘:SoA 让同一维度的数值连续排布,编译器更容易自动向量化,一条 SIMD 指令处理多个元素。
- 图像张量:NCHW 和 NHWC 之争本质就是"通道维度是否连续",同样的卷积核,换个排布可能顺序读也可能跳着读。换布局(如转成 channels-last)常带来可观的加速,代价是一次重排。
- 多线程:两个线程频繁改同一缓存行里的不同变量,会互相把对方的缓存打失效,这叫伪共享(false sharing),靠 padding 或对齐隔离。
怎么用
先量后调。多数框架有现成的连续化接口和布局开关,改一行可能比调算法收益更大;但盲目改布局也可能引入一次昂贵的拷贝。真正的判据是你的访问模式:只碰一列就选 SoA,总是整对象一起用就选 AoS。具体行为以官方文档和实测为准,别凭直觉下结论,先 profile。
