SIMD卷积数据重排优化完整技术文档
项目背景
第二届开放原子大赛 - Tecorigin算子优化挑战赛
- 项目名称: tecoalConvolutionForward算子性能优化
- 优化目标: 解决I/O瓶颈问题(93.1%耗时占比)
- 核心成果: 实现3.7倍整体性能提升(1820ms → 489ms)
- SIMD优化贡献: 547.88ms性能提升,占总优化30%+
第一部分:问题根本原因分析
1.1 卷积算法的两种视角冲突
数学定义视角 (最终期望格式)
卷积的数学定义:
Output[n][h][w][m] = Σ(Input[n][h'][w'][c] * Weight[c][r][s][m])
c,r,s
期望的内存布局 (NHWC格式):
[N0H0W0完整M通道] [N0H0W1完整M通道] [N0H0W2完整M通道] ...
高效计算视角 (矩阵乘法实现)
卷积 → 矩阵乘法:
[HW个空间位置 × C通道] × [C通道 × M输出通道] = [HW × M输出矩阵]
矩阵乘法库的优化策略:
- 按32个通道为一组进行分块计算
- 利用SIMD指令并行处理32个输出通道
- 输出格式:[HW位置的第一个32通道块] [HW位置的第二个32通道块] ...
1.2 具体数据布局对比
场景假设: 输出尺寸2×2,128个输出通道
矩阵乘法库输出格式 (y_buf) - 计算最优格式
内存布局 (分块计算结果):
┌─────────────────────────────────────────────────── ──────┐
│ Block 0 (32通道) │
├─────────────────────────────────────────────────────────┤
│ (0,0)位置 │ (0,1)位置 │ (1,0)位置 │ (1,1)位置 │
│ 通道0-31 │ 通道0-31 │ 通道0-31 │ 通道0-31 │
├─────────────────────────────────────────────────────────┤
│ Block 1 (32通道) │
├─────────────────────────────────────────────────────────┤
│ (0,0)位置 │ (0,1)位置 │ (1,0)位置 │ (1,1)位置 │
│ 通道32-63 │ 通道32-63 │ 通道32-63 │ 通道32-63 │
├─────────────────────────────────────────────────────────┤
│ Block 2 (32通道) │
├─────────────────────────────────────────────────────────┤
│ (0,0)位置 │ (0,1)位置 │ (1,0)位置 │ (1,1)位置 │
│ 通道64-95 │ 通道64-95 │ 通道64-95 │ 通道64-95 │
├─────────────────────────────────────────────────────────┤
│ Block 3 (32通道) │
├─────────────────────────────────────────────────────────┤
│ (0,0)位置 │ (0,1)位置 │ (1,0)位置 │ (1,1)位置 │
│ 通道96-127 │ 通道96-127 │ 通道96-127 │ 通道96-127 │
└─────────────────────────────────────────────────────────┘
线性内存地址:
0-31: (0,0)通道0-31 32-63: (0,1)通道0-31 64-95: (1,0)通道0-31
96-127: (1,1)通道0-31 128-159: (0,0)通道32-63 160-191: (0,1)通道32-63
192-223: (1,0)通道32-63 224-255: (1,1)通道32-63
256-287: (0,0)通道64-95 288-319: (0,1)通道64-95 320-351: (1,0)通道64-95
352-383: (1,1)通道64-95 384-415: (0,0)通道96-127 416-447: (0,1)通道96-127
448-479: (1,0)通道96-127 480-511: (1,1)通道96-127
用户期望格式 (out_buf) - 标准NHWC格式
内存布局 (按空间位置连续存储):
┌─────────────────────────────────────────────────────────┐
│ 按空间位置连续存储 │
├─────────────────────────────────────────────────────────┤
│ (0,0)位置完整128通道 │ (0,1)位置完整128通道 │
├─────────────────────────────────────────────────────────┤
│ (1,0)位置完整128通道 │ (1,1)位置完整128通道 │
└─────────────────────────────────────────────────────────┘
线性内存地址:
0-127: (0,0)的完整128通道 128-255: (0,1)的完整128通道
256-383: (1,0)的完整128通道 384-511: (1,1)的完整128通道
格式差异的核心问题
矩阵乘法库格式的特点:
- 优势: SIMD并行计算友好,32个通道一组,便于向量化处理
- 问题: 访问单个位置的完整通道需要跳跃式内存访问
标准NHWC格式的特点:
- 优势: 单个位置的所有通道连续存储,符合深度学习框架期望
- 问题: 如果强制矩阵乘法库直接输出此格式,会破坏其内部SIMD优化
1.3 不做数据重排的后果
内存访问效率低下
// 访问位置(0,0)的完整128通道需要这样:
float channels[128];
channels[0-31] = y_buf[0:32]; // 第一个32通道块
channels[32-63] = y_buf[128:160]; // 跳跃到第二个块
channels[64-95] = y_buf[256:288]; // 跳跃到第三个块
channels[96-127] = y_buf[384:416]; // 跳跃到第四个块
// 4次非连续内存访问,缓存效率极低!
与标准框架不兼容
# 深度学习框架期望的张量格式
tensor = torch.zeros(1, 2, 2, 128) # [N, H, W, C]
# 非标准格式会导致数值计算错误和性能下降
第二部分:SIMD数据重排技术方案
2.1 核心技术实现
数据流向设计
完整的数据重排流程:
┌─────────────┐ simd_load ┌─────────────────┐ simd_store ┌─────────────┐
│ y_buf │ ===============> │ SIMD Register │ ===============> │ out_buf │
│ (SPM内存) │ │ (向量寄存器) │ │ (SPM内存) │
└─────────────┘ └─────────────────┘ └─────────────┘
矩阵乘法输出 临时向量存储 重排后的数据
分散的32通道块 16个元素并行 连续的通道布局