UB 缓冲区管理指南
【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体,本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills
TBuf/TQue 选择、Double Buffer 流水线并行、批量搬运模式。
目录
- TBuf vs TQue 选择
- TQue 详解
- TBuf 详解
- Double Buffer 流水线并行
- 批量搬运 + 逐行计算模式
TBuf vs TQue 选择
| 场景 | 推荐类型 | 说明 |
|---|---|---|
| MTE2/MTE3 搬运缓冲区 | TQue<VECIN/VECOUT> | 需要与 Vector 并行,需要 EnQue/DeQue |
| 纯 Vector 计算缓冲区 | TBuf<VECCALC> | 不涉及 MTE 搬运,用Get<T>()获取 |
| Double Buffer | TQue+InitBuffer(que, 2, size) | 在 InitBuffer 中设置 num=2 开启 |
TQue 详解
模板参数
template <TPosition pos, int32_t depth, auto mask = 0> class TQue;| 参数 | 说明 |
|---|---|
pos | 队列逻辑位置:VECIN,VECOUT,A1,A2,B1,B2,CO1,CO2 |
depth | 队列深度,表示可连续 EnQue/DeQue 的次数 |
mask | 数据格式转换(ND↔NZ)或编译期优化参数 |
depth 参数关键说明
| depth 值 | 适用场景 | 说明 |
|---|---|---|
depth=1 | 默认推荐,非 Tensor 原地操作 | 编译器有特殊优化,性能更好 |
depth=0 | Tensor 原地操作 | 需要设置 |
depth=2 | 连续 2 次 EnQue 场景 | 与 InitBuffer 的 num 参数独立 |
注意:depth与 Double Buffer 无关。Double Buffer 由InitBuffer的num参数控制。
// ✅ 非连续入队(普通场景):depth=1 即可 AscendC::TQue<AscendC::TPosition::VECIN, 1> que; pipe->InitBuffer(que, 1, size); auto tensor = que.AllocTensor<T>(); que.EnQue(tensor); tensor = que.DeQue<T>(); que.FreeTensor(tensor);Double Buffer 配置
Double Buffer 是在InitBuffer的num参数中设置,与模板参数depth无关。
| InitBuffer 参数 | 作用 | 说明 |
|---|---|---|
InitBuffer(que, num, size) | num控制 Double Buffer | num=1=单 Buffer,num=2=开启 Double Buffer |
模板参数depth | 队列深度 | 表示可连续 EnQue 的次数 |
// ✅ 开启 Double Buffer:在 InitBuffer 中设置 num=2 AscendC::TQue<AscendC::TPosition::VECIN, 1> que; // 模板 depth=1 即可 pipe->InitBuffer(que, 2, size); // num=2 开启 Double Buffer // ✅ 关闭 Double Buffer AscendC::TQue<AscendC::TPosition::VECIN, 1> que; pipe->InitBuffer(que, 1, size); // num=1 单 BufferTQue Buffer 数量限制
| 产品系列 | eventID 数量 | 最大 TQue 数量 |
|---|---|---|
| Atlas 训练系列 | 4 | 4 |
| Atlas 推理系列 AI Core | 8 | 8 |
| Atlas 推理系列 Vector Core | 8 | 8 |
| Atlas A2/A3 系列 | 8 | 8 |
注意:
- 不开启 Double Buffer(num=1):最多可申请 8 个 TQue
- 开启 Double Buffer(num=2):每个 TQue 占用 2 个 buffer,最多只能申请 4 个 TQue
// 开启 Double Buffer 时,最多只能申请 4 个 TQue pipe->InitBuffer(que0, 2, size); // ✅ pipe->InitBuffer(que1, 2, size); // ✅ pipe->InitBuffer(que2, 2, size); // ✅ pipe->InitBuffer(que3, 2, size); // ✅ pipe->InitBuffer(que4, 2, size); // ❌ 超过限制TQue 正确用法
// TQue:需要队列管理(MTE 搬运相关) // 模板 depth=1 即可,Double Buffer 在 InitBuffer 的 num 参数中设置 AscendC::TQue<AscendC::TPosition::VECIN, 1> inQueueX; pipe->InitBuffer(inQueueX, 2, bufferSize); // num=2 开启 Double Buffer AscendC::LocalTensor<half> x = inQueueX.AllocTensor<half>(); AscendC::DataCopyPad(x, xGm, {1, size * sizeof(half), 0, 0}, {false, 0, 0, 0}); inQueueX.EnQue(x); // ... AscendC::LocalTensor<half> xLocal = inQueueX.DeQue<half>(); inQueueX.FreeTensor(xLocal);TBuf 详解
特性
| 特性 | 说明 |
|---|---|
| 内存用途 | 只能参与计算,无法执行 EnQue/DeQue |
| 内存分配 | 每次 InitBuffer 只分配一块内存 |
| Tensor 释放 | 无需手动释放 |
// TBuf:纯计算缓冲区 AscendC::TBuf<AscendC::TPosition::VECCALC> workBuf; pipe->InitBuffer(workBuf, bufferSize); // ✅ 使用 Get<T>() 获取 Tensor,无需释放 AscendC::LocalTensor<float> work = workBuf.Get<float>(); // ... 计算逻辑 ... // 无需 FreeTensorDouble Buffer 流水线并行
核心认知
Double Buffer 不是"用2块内存计算",而是"用2块内存做搬入/搬出,使 MTE2/MTE3 与 Vector 计算并行"。
本质:内存搬运与计算并行,掩盖搬运延迟。
硬件原理
- MTE2:搬运工,GM → UB
- Vector:加工员,计算
- MTE3:搬运工,UB → GM
时间线对比
无 Double Buffer(串行):
Row 0: [MTE2][Vector][MTE3] Row 1: [MTE2][Vector][MTE3]有 Double Buffer(并行):
Row 0: [MTE2-B0][Vector-B0][MTE3-B0] Row 1: [MTE2-B1][Vector-B1][MTE3-B1] ↑ MTE2与Vector并行!实现原则
| Buffer 类型 | InitBuffer num | 说明 |
|---|---|---|
TQue<VECIN>(MTE2 搬运) | 2 | num=2 开启 Double Buffer,与 Vector 并行 |
TQue<VECOUT>(MTE3 搬运) | 2 | num=2 开启 Double Buffer,与 Vector 并行 |
TBuf<VECCALC>(纯计算) | - | TBuf 不涉及 MTE 搬运 |
正确用法
// 1. Init: num=2 开启 Double Buffer pipe->InitBuffer(inQueueX, 2, tileSize * sizeof(T)); pipe->InitBuffer(outQueueY, 2, tileSize * sizeof(T)); pipe->InitBuffer(workBuf, workSize * sizeof(T)); // 2. Process: 单循环结构,TQue 自动轮转 for (int i = 0; i < totalTiles; i++) { CopyIn(i); // MTE2 异步搬运 Compute(i); // Vector 计算 CopyOut(i); // MTE3 异步搬出 } // 3. CopyIn void CopyIn(int i) { LocalTensor<T> x = inQueueX.AllocTensor<T>(); DataCopyPad(x, xGm[i * tileSize], {1, (uint32_t)(tileSize * sizeof(T)), 0, 0}, {false, 0, 0, 0}); inQueueX.EnQue(x); } // 4. Compute void Compute(int i) { LocalTensor<T> x = inQueueX.DeQue<T>(); LocalTensor<T> y = outQueueY.AllocTensor<T>(); Add(y, x, constTensor, tileSize); outQueueY.EnQue(y); inQueueX.FreeTensor(x); } // 5. CopyOut void CopyOut(int i) { LocalTensor<T> y = outQueueY.DeQue<T>(); DataCopyPad(yGm[i * tileSize], y, {1, (uint32_t)(tileSize * sizeof(T)), 0, 0}); outQueueY.FreeTensor(y); }为什么能并行?
| 操作 | 特性 |
|---|---|
DataCopy | 异步 DMA,立即返回 |
EnQue | 非阻塞,标记就绪 |
DeQue | 阻塞,等待就绪 |
常见误区
| 误区 | 正确理解 |
|---|---|
| 需要手动拆成 Ping/Pong 两套代码 | 单循环 +InitBuffer(que, 2, size)自动管理 |
| depth 模板参数控制 Double Buffer | Double Buffer 由InitBuffer的num参数控制 |
| depth 越大越好 | 模板 depth 通常设为 1,性价比最高 |
| 所有 buffer 都要 num=2 | 只有涉及 MTE 搬运的才需要 Double Buffer |
批量搬运 + 逐行计算模式
适用场景
处理多行数据时,批量搬运减少 MTE2/MTE3 调用次数,充分利用带宽。
模式结构
CopyInBatch(N行) → 逐行计算(N次) → CopyOutBatch(N行)代码模板
__aicore__ inline void ProcessBatch() { uint32_t totalRowsToProcess = endRow - startRow; if (totalRowsToProcess == 0) return; for (uint32_t tile = 0; tile < tilesPerCore; tile++) { uint32_t startLocalRow = tile * tileRows; // 边界检查:防止 uint32_t 下溢 if (startLocalRow >= totalRowsToProcess) break; uint32_t remaining = totalRowsToProcess - startLocalRow; uint32_t rowsThisTile = (remaining < tileRows) ? remaining : tileRows; CopyInBatch(startLocalRow, rowsThisTile); ComputeBatch(rowsThisTile); CopyOutBatch(startLocalRow, rowsThisTile); } }Host 侧 Tiling 计算
// A2/A3 UB = 192KB constexpr uint64_t UB_SIZE = 192 * 1024; constexpr uint32_t MAX_BLOCK_COUNT = 4095; // DataCopyPad blockCount 限制 // bytesPerTileRow: double buffer (in*2 + out*2) uint32_t bytesPerTileRow = paddedColsT * typeSizeBytes * 4; // tileRows uint32_t tileRows = (UB_SIZE - overheadBytes) / bytesPerTileRow; tileRows = std::max(1u, std::min(tileRows, MAX_BLOCK_COUNT));注意事项
- tileRows 限制:DataCopyPad 的
blockCount最大 4095 - 尾核处理:
startLocalRow >= totalRowsToProcess时提前退出 - stride 计算:UB 侧 stride 单位是 32 字节块,GM 侧是字节
【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体,本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考