CANN算术运算API优化指南
算术运算 API 优化指南【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills适用场景使用算术运算 APIAdd/Sub/Mul/Div时选择最优实现方式避免不必要的广播 buffer 和指令开销。目录概述场景1标量操作单行方案对比API 接口完整示例场景2广播操作多行方案对比-1核心原理分批处理场景3半精度加减法精度优化问题根因默认策略标准范式Kernel 集成要点性能对比适用 API常见错误概述算术运算 APIAdd/Sub/Mul/Div支持两种使用模式模式API适用场景Buffer 需求标量操作Adds/Muls单行处理Softmax AR 模板32B广播操作Sub/Div BinaryRepeatParams多行处理Softmax ARA 模板alignedCols×4关键优化单行使用Adds/Muls避免 Duplicate多行使用src1RepStride0避免逐行循环场景1标量操作单行方案对比问题需要对 tensor 每个元素执行x - scalar或x / scalar典型场景Softmax AR 模板x - max_val数值稳定Softmax AR 模板exp(x) / sum归一化LayerNormx - mean中心化BatchNormx * gamma beta方案对比方案指令数Buffer 需求推荐度Duplicate Sub2 条rLength × sizeof(T)⭐⭐Duplicate Div2 条rLength × sizeof(T)⭐⭐Adds(-scalar)1 条32B⭐⭐⭐⭐⭐Muls(1/scalar)1 条32B⭐⭐⭐⭐⭐API 接口Adds标量加法template typename T, bool isSetMask true __aicore__ inline void Adds( const LocalTensorT dst, const LocalTensorT src, const T scalarValue, const int32_t count); // 功能: dst[i] src[i] scalarValue // 示例: Adds(dst, src, -maxVal, count) // 减法转加法Muls标量乘法template typename T, bool isSetMask true __aicore__ inline void Muls( const LocalTensorT dst, const LocalTensorT src, const T scalarValue, const int32_t count); // 功能: dst[i] src[i] * scalarValue // 示例: Muls(dst, src, 1.0/sum, count) // 除法转乘法完整示例优化前Sub/Div Duplicate// Buffer 初始化 uint32_t broadcastBufSize rLengthAlign * sizeof(T); // 例如512B (rLength128, FP32) pipe.InitBuffer(broadcastBuf, broadcastBufSize); pipe.InitBuffer(reduceBuf, reduceBufSize); // Compute LocalTensorT broadcastLocal broadcastBuf.GetT(); for (uint32_t row 0; row rowsThisLoop; row) { uint32_t rowOffset row * rLengthAlign; // Step 1: ReduceMax ReduceMaxT(broadcastLocal, xLocal[rowOffset], reduceTmpLocal, rLength, false); // Step 2: Duplicate Sub需要广播 buffer T maxVal broadcastLocal.GetValue(0); DuplicateT(broadcastLocal, maxVal, rLength); // 指令 1 SubT(yLocal[rowOffset], xLocal[rowOffset], broadcastLocal, rLength); // 指令 2 // Step 3: Exp ExpT(yLocal[rowOffset], yLocal[rowOffset], rLength); // Step 4: ReduceSum ReduceSumT, true(broadcastLocal, yLocal[rowOffset], reduceTmpLocal, rLength); // Step 5: Duplicate Div需要广播 buffer T sumVal broadcastLocal.GetValue(0); DuplicateT(broadcastLocal, sumVal, rLength); // 指令 3 DivT(yLocal[rowOffset], yLocal[rowOffset], broadcastLocal, rLength); // 指令 4 } // 总计6 条指令/行需要 broadcastBuf (512B for rLength128)优化后Adds/Muls 标量// Buffer 初始化节省 broadcastBuf uint32_t scalarBufSize 32; // 最小对齐要求仅需存储 1 个标量 pipe.InitBuffer(scalarBuf, scalarBufSize); pipe.InitBuffer(reduceBuf, reduceBufSize); // Compute LocalTensorT scalarLocal scalarBuf.GetT(); for (uint32_t row 0; row rowsThisLoop; row) { uint32_t rowOffset row * rLengthAlign; // Step 1: ReduceMax ReduceMaxT(scalarLocal, xLocal[rowOffset], reduceTmpLocal, rLength, false); // Step 2: Adds直接标量操作无需广播 T maxVal scalarLocal.GetValue(0); AddsT(yLocal[rowOffset], xLocal[rowOffset], -maxVal, rLength); // 指令 1 // Step 3: Exp ExpT(yLocal[rowOffset], yLocal[rowOffset], rLength); // Step 4: ReduceSum ReduceSumT, true(scalarLocal, yLocal[rowOffset], reduceTmpLocal, rLength); // Step 5: Muls除法转乘法直接标量操作 T sumVal scalarLocal.GetValue(0); T invSumVal (T)1.0 / sumVal; // CPU 端计算 1/sum MulsT(yLocal[rowOffset], yLocal[rowOffset], invSumVal, rLength); // 指令 2 } // 总计4 条指令/行节省 broadcastBuf (480B for rLength128)场景2广播操作多行方案对比问题需要对多行数据执行相同的标量操作如x - max、exp / sum方案对比方案API 调用Buffer 需求推荐度逐行循环R 次alignedCols×4⭐⭐单次广播R ≤ 641 次alignedCols×4⭐⭐⭐⭐⭐分批广播R 64ceil(R/64) 次alignedCols×4⭐⭐⭐⭐⭐核心原理BinaryRepeatParams.src1RepStride0 实现广播struct BinaryRepeatParams { uint8_t dstBlkStride; // 单次迭代内dst 的 block 步长 uint8_t src0BlkStride; // 单次迭代内src0 的 block 步长 uint8_t src1BlkStride; // 单次迭代内src1 的 block 步长 uint8_t dstRepStride; // 相邻迭代间dst 的 block 步长 uint8_t src0RepStride; // 相邻迭代间src0 的 block 步长 uint8_t src1RepStride; // 0 实现广播 };工作原理dstRepStride alignedCols/8每次迭代dst 前进alignedCols个元素src0RepStride alignedCols/8每次迭代src0 前进alignedCols个元素src1RepStride 0每次迭代src1不前进重复读取相同位置效果迭代 0: dst[0:cols] src0[0:cols] - src1[0:cols] 迭代 1: dst[cols:2cols] src0[cols:2cols] - src1[0:cols] ← 重复读取 迭代 2: dst[2cols:3cols] src0[2cols:3cols]- src1[0:cols] ← 重复读取分批处理方案1逐行循环低效for (uint32_t r 0; r R; r) { Sub(dstLocal[r * alignedCols], srcLocal[r * alignedCols], scalarLocal, alignedCols); } // API 调用R 次方案2单次广播高效R ≤ 64uint64_t mask alignedCols; uint8_t repeatTime R; Sub(dstLocal, srcLocal, scalarLocal, mask, repeatTime, {1, 1, 1, alignedCols/8, alignedCols/8, 0}); // API 调用1 次 // 性能提升R 倍方案3分批广播高效R 64constexpr uint32_t BATCH_SIZE 64; uint32_t totalBatches (R BATCH_SIZE - 1) / BATCH_SIZE; // ceil(R/64) for (uint32_t batch 0; batch totalBatches; batch) { uint32_t startRow batch * BATCH_SIZE; uint8_t repeatTime (startRow BATCH_SIZE R) ? BATCH_SIZE : (R - startRow); uint32_t offset startRow * alignedCols; Sub(dstLocal[offset], srcLocal[offset], scalarLocal, mask, repeatTime, {1, 1, 1, alignedCols/8, alignedCols/8, 0}); } // API 调用ceil(R/64) 次 // 性能提升约 64 倍场景3半精度加减法精度优化问题根因半精度FP1610 位尾数BF167 位两数量级差异大时会大数吃小数Add 和 Sub 面临相同风险a 1024.0, b 0.0625 Addhalf : 1024.0 ← b 被丢弃 Subhalf : 1024.0 ← b 被丢弃 Addfloat : 1024.0625 ← 正确 Subfloat : 1023.9375 ← 正确临界比值显著退化阈值FP16 ≈ 2¹⁰1024BF16 ≈ 2⁷128完全丢失阈值约 2×尾数隐含 1 位。累加 N 次后阈值除以 √N。默认策略spec 未明确输入同量级时一律升 FP32。通用算子调用方分布未知一旦遇到残差/累加/归一化/量化反量化即不可控。Add 和 Sub 适用同一规则BF16 和 FP16 仅临界比值不同见下。spec 声明输入同量级推荐实现理由否默认Cast → Add/Subfloat(in-place) → Cast覆盖所有分布是mask 叠加、已归一化概率相加等直接Add/Subhalf两输入本身仅 10/7 位精度单次运算不引入额外损失无 √N 累加放大标准范式Add/Subfloat(dst, src0, src1)支持 dst 与 src 别名仅需K2 份FP32 临时空间dst 复用 src0Fp32// GetT(len) 的 len 是元素数偏移用 tensor[N] auto src0Fp32 tmpBuf.Getfloat(TILE); auto src1Fp32 src0Fp32[TILE]; // half → float 用 CAST_NONEfloat → half 用 CAST_ROUND AscendC::Castfloat, half(src0Fp32, src0, AscendC::RoundMode::CAST_NONE, count); AscendC::Castfloat, half(src1Fp32, src1, AscendC::RoundMode::CAST_NONE, count); AscendC::Addfloat(src0Fp32, src0Fp32, src1Fp32, count); // in-placeSub 同理 AscendC::Casthalf, float(dst, src0Fp32, AscendC::RoundMode::CAST_ROUND, count);代价3 条指令共 4 条2 Cast↑ 1 Add/Sub 1 Cast↓K×count×sizeof(float) UB。BF16 路径将half替换为bfloat16_t即可。API 别名约束决定 KAdd/Subfloat在 Vector 上支持 dst 与 src 别名故 K2Reduce 类 API 禁止 dsttmpBuffer不可类比。Kernel 集成要点升精度路径需要 K2 份 FP32 临时 BufferAdd/Sub 支持 dst/src 别名故 dst 复用 src0Fp32。精度转换 RoundMode 详见 api-precision.md。性能对比标量操作单行项目优化前优化后改善指令数/行6 条4 条-33%Buffer 大小512B (rLength128)32B-94%UB 节省-~480B可用于更大 rowsPerLoop广播操作多行R (行数)逐行循环单次广播分批广播性能提升3232 次1 次-32×6464 次1 次-64×100100 次-2 次50×128128 次-2 次64×200200 次-4 次50×半精度加减法FP16/BF16 Add/Sub升精度路径相对直接Add/Subhalf3 条指令共 4 条、2×count×sizeof(float) UB。适用场景见场景3 默认策略。实测示例Softmax ARA 分支场景R128, alignedCols64, FP32操作优化前优化后提升Sub (x-max)128 次2 次64×Div (exp/sum)128 次2 次64×总计256 次4 次64×适用 API所有支持BinaryRepeatParams的二元运算 APIAPI用途单行优化多行优化Add加法Addssrc1RepStride0Sub减法Adds(-val)src1RepStride0Mul乘法Mulssrc1RepStride0Div除法Muls(1/val)src1RepStride0Max最大值-src1RepStride0Min最小值-src1RepStride0常见错误错误原因解决方案编译错误mask 超限mask 64(FP32)分批处理或回退循环数据错误src1RepStride未设置为 0确认参数{..., 0}部分行正确offset 计算错误offset startRow * alignedCols越界崩溃repeatTime 计算错误使用三目运算Buffer 不足使用 Duplicate 方案改用 Adds/Mulsdst tmpBufferReduce API 限制使用不同 bufferFP16/BF16 加减法精度丢失直接Add/Subhalf大数吃小数升精度Cast→FP32 Add/Sub(in-place)→Cast半精度加减法 Cast 后越界临时 Buffer 不足预留2 × count × sizeof(float)Add/Sub 复用 src0Fp32GetT(len)取出长度异常误把字节数当成元素数len是元素数不是字节数检查清单使用算术运算 API 时确保标量操作单行使用Adds(-scalar)替代Duplicate Sub使用Muls(1/scalar)替代Duplicate Div标量除法转换为乘法CPU 端计算 1/scalar广播操作多行alignedCols ≤ 64 (FP32) / ≤ 128 (FP16)使用src1RepStride 0实现广播R 64 时使用分批处理offset 计算正确offset startRow * alignedCols半精度加减法FP16/BF16 Add/Sub默认升精度仅当 spec 明确输入同量级时才允许直接Add/Subhalf临时 Buffer 预留K × count × sizeof(float)Add/Subfloat支持别名故 K2dst 复用 src0Fp32in-placeGetT(len)的 len 是元素数偏移用tensor[N]Cast 方向half→float用CAST_NONEfloat→half用CAST_ROUND参考资料BinaryRepeatParams 结构体Adds APIMuls APISub APIDiv API【免费下载链接】cannbot-skillsCANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体本仓库为其提供可复用的 Skills 模块。项目地址: https://gitcode.com/cann/cannbot-skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2599333.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!