阶段 4:内存优化 — 详细参考
存储层级一览
| Buffer | 用途 | 说明 |
|---|---|---|
| GM(HBM) | 全局内存 | 带宽约 1.6 TB/s |
| L2Cache | 共享缓存 | 约 192 MB,带宽约 7 TB/s |
| L1 Buffer | AI Core 本地存储 | Cube 数据中转 |
| L0A / L0B | Cube 输入 | 由 L1 加载 |
| L0C(CO1) | Cube 输出 | 支持原地累加 |
| UB(Unified Buffer) | Vector 输入/输出 | VECIN, VECOUT, VECCALC |
| BT Buffer(C2) | Bias 表 | 仅分离架构 |
| FP Buffer(C2PIPE2GM) | Fixpipe 参数 | 仅分离架构 |
4.1 UB Buffer 融合
连续多次 Vector 运算时,将中间结果保留在 UB 上,不经 GM 往返。
n 次连续运算的 GM 搬运次数从 2n 降为 2。
反例 — 每次运算都经 GM 往返(Exp + Abs 需 4 次 GM 搬运):
class KernelSample {
__aicore__ inline void Process() {
CopyIn(); // GM → UB
Compute(); // Exp
CopyOut(); // UB → GM
CopyIn1(); // GM → UB(重新读回 Exp 结果)
Compute1(); // Abs
CopyOut1(); // UB → GM
}
};
正例 — 在 UB 内链式计算(仅 2 次 GM 搬运):
class KernelSample {
__aicore__ inline void Compute() {
LocalTensor<float> src0Local = inQueueSrc0.DeQue<float>();
LocalTensor<float> dstLocal = outQueueDst.AllocTensor<float>();
Exp(dstLocal, src0Local, 1024);
Abs(dstLocal, dstLocal, 1024); // 原地操作,留在 UB
outQueueDst.EnQue<float>(dstLocal);
inQueueSrc0.FreeTensor(src0Local);
}
__aicore__ inline void Process() {
CopyIn(); // GM → UB(一次)
Compute(); // Exp + Abs 融合
CopyOut(); // UB → GM(一次)
}
};
4.2 L0C 累加矩阵乘
A1*B1 + A2*B2 + ... 场景下,利用 Mmad 的内建累加功能将部分结果保留在
CO1(L0C)中。避免每次矩阵乘结果都 CO1→GM→UB 再做 Add。
反例 — 逐次搬出后在 UB 求和:
void Process() {
Compute(); // Mmad → CO1
CopyOut(); // CO1 → workspace(GM)
CopyIn1(); // workspace → UB
Compute1(); // Mmad → CO1
CopyOut1(); // CO1 → workspace(GM)
CopyIn2(); // workspace → UB
Compute2(); // Add(result1, result2) in UB
CopyOut2(); // UB → GM
}
正例 — 在 L0C 中原地累加:
void Compute() {
MmadParams mmadParams;
mmadParams.m = m; mmadParams.n = n; mmadParams.k = k;
Mmad(c1Local, a2Local_1, b2Local_1, mmadParams);
mmadParams.cmatrixInitVal = false;
Mmad(c1Local, a2Local_2, b2Local_2, mmadParams); // 在 CO1 原地累加
}
// 最后一次 CopyOut: CO1 → GM
4.3 小矩阵长驻 L1
当 L1 无法同时容纳左右矩阵(如左矩阵 992K、右矩阵 16K、L1 容量 512K)时, 将较小矩阵一次加载后常驻 L1,仅循环搬运较大矩阵。
反例 — 每次迭代都重新加载两个矩阵:
void Process() {
for (uint32_t i = 0; i < 2; i++) {
CopyInA1(i); // 加载左矩阵切片
SplitA();
for (uint32_t j = 0; j < 2; j++) {
CopyInB1(j); // 每次都重新加载右矩阵
SplitB();
Compute(i, j);
}
}
CopyOut();
}
正例 — 右矩阵一次加载,仅循环搬运左矩阵:
void Process() {
CopyInB1(); // 右矩阵一次全载入 L1
SplitB(); // L1 → L0B
for (uint32_t i = 0; i < 2; i++) {
CopyInA1(i); // 循环加载左矩阵切片
SplitA();
for (uint32_t j = 0; j < 2; j++) {
Compute(i, j); // 右矩阵已在 L0B
}
}
CopyOut();
}
2 个左矩阵切片时:搬运次数从 4+4=8 降为 1+2=3。
4.4 BT Buffer 存放 bias(分离架构)
将 bias 存入 BT Buffer(C2),在 Mmad 中一步融合 bias 加法,避免 CO1→GM→UB→Add→GM 的冗长路径。
反例 — 在 UB 中单独做 bias Add:
TQue<QuePosition::VECIN, 1> inQueueBias;
// Mmad 后:CO1 → workspace(GM) → UB
// bias: GM → UB
// Add(matmul_result, bias) in UB → GM
正例 — 通过 BT Buffer 融合:
TQue<QuePosition::C1, 1> inQueueC1; // L1
TQue<QuePosition::C2, 1> outQueueC2; // BT Buffer
void SplitBias() {
LocalTensor<float> bias1Local = inQueueC1.DeQue<float>();
LocalTensor<float> bias2Local = outQueueC2.AllocTensor<float>();
// L1 → BT
DataCopy(bias2Local, bias1Local, {1, (uint16_t)(n * sizeof(float) / 64), 0, 0});
outQueueC2.EnQue<float>(bias2Local);
inQueueC1.FreeTensor(bias1Local);
}
void Compute() {
LocalTensor<float> bias2Local = outQueueC2.DeQue<float>();
MmadParams mmadParams;
mmadParams.m = m; mmadParams.n = n; mmadParams.k = k;
mmadParams.cmatrixInitVal = false;
Mmad(c1Local, a2Local, b2Local, bias2Local, mmadParams); // 融合 bias
outQueueC2.FreeTensor(bias2Local);
}
4.5 FP Buffer 存放量化参数(分离架构)
将量化参数存入 FP Buffer(C2PIPE2GM),通过 Fixpipe 在搬出路径上随路量化。 避免 CO1→GM→UB→量化计算→GM 的冗长路径。
反例 — 在 UB 中单独做量化:
TQue<QuePosition::VECIN, 1> inQueueDeq; // 量化参数在 UB
// CO1 → workspace → UB
// 量化参数: GM → UB
// Cast + Mul + Cast in UB
// 结果 → GM
正例 — 通过 FP Buffer 融合:
TQue<QuePosition::C1, 1> inQueueDeq1; // L1
TQue<QuePosition::C2PIPE2GM, 1> inQueueDeq; // FP Buffer
void SplitDeq() {
LocalTensor<uint64_t> deq1Local = inQueueDeq1.DeQue<uint64_t>();
LocalTensor<uint64_t> deqLocal = inQueueDeq.AllocTensor<uint64_t>();
// L1 → FP Buffer
DataCopy(deqLocal, deq1Local, {1, (uint16_t)(cSize * sizeof(uint64_t) / 128), 0, 0});
inQueueDeq.EnQue<uint64_t>(deqLocal);
inQueueDeq1.FreeTensor(deq1Local);
}
void CopyOut() {
LocalTensor<float> c1Local = outQueueCO1.DeQue<float>();
LocalTensor<uint64_t> deqLocal = inQueueDeq.DeQue<uint64_t>();
SetFixpipeNz2ndFlag(1, 0, 0);
DataCopyCO12DstParams params;
params.nSize = n;
params.mSize = m;
params.srcStride = m;
params.dstStride = n;
params.quantPre = QuantMode_t::VQF322B8_PRE;
params.nz2ndEn = true;
DataCopy(cGM, c1Local, params); // 搬出时随路量化
outQueueCO1.FreeTensor(c1Local);
}