CANN-ops-math类型转换算子-昇腾NPU上fp16和bf16怎么互转才不拖后腿混合精度训练在每层要做 4-6 次类型转换fp16 计算 → fp32 归一化 → fp16 存储。推理也有 bf16 权重转 fp16 计算的场景。类型转换Cast的频率比大部分人的直觉高得多ops-math 的 Cast 实现如果不够快混合精度的收益会被转换开销吃掉。Cast 的两种实现路径路径 1DMA 引擎转换。昇腾NPU的 DMA 引擎在搬运数据时可以直接做格式转换。数据从源地址搬到目标地址的过程中硬件自动完成 fp16→fp32 或 fp32→fp16 的转换。不占 Vector 单元不占 Cube 单元。路径 2Vector 单元转换。当数据不是连续存储时比如经过 transpose 或 stride 操作DMA 无法直接转换需要 Vector 单元逐元素处理。连续 tensor DMA 搬运 转换带宽利用率 95% 非连续 tensor DMA 搬运 → Vector 转换 → DMA 写回带宽利用率 30-40%3 倍的带宽差距。这就是为什么 ops-math 的文档反复强调确保 tensor 是 contiguous 的。fp16 vs bf16选哪个昇腾NPU对 fp16 和 bf16 都有原生支持。两者在 Cast 时的开销一样选择取决于你的模型精度范围精度适用场景fp16±65504约 3 位十进制推理为主训练需 loss scalingbf16±3.4×10^38约 2 位十进制训练为主不需要 loss scalingfp32±3.4×10^38约 7 位十进制归一化、loss 计算bf16 的范围跟 fp32 一样大训练时不容易溢出不需要 loss scaling。但精度比 fp16 低 1 位——推理时 bf16 的输出质量可能略差于 fp16。在昇腾NPU上Llama 系列模型推荐 fp16 推理社区权重大多是 fp16训练推荐 bf16。Cast 在 Attention 里的位置FlashAttention 内部有两次 Cast1. Q·K^T 的结果fp16→ Cast → fp32 → Softmax → Cast → fp16 Softmax 的指数运算需要 fp32 精度fp16 会溢出 2. Attention 输出fp16→ 直接传给下一个算子不需要 Cast这两次 Cast 在 FlashAttention 融合 kernel 内部完成不走独立的 Cast 算子。数据在 Vector 单元的 local buffer 里完成 fp16↔fp32 转换不需要 HBM 读写。如果你用的是标准 Attention非融合每次 Softmax 前后的 Cast 是独立 kernel每次约 0.02ms。32 层 × 2 次 0.64ms 的额外延迟。融合后这部分为零。混合精度训练的 Cast 热点训练时每层的 Cast 次数1. MatMul 输入 fp16→fp32CUBLAS/torch_npu 自动处理对用户透明 2. MatMul 输出 fp32→fp16同上 3. LayerNorm/RMSNorm输入 fp16→fp32输出 fp32→fp16 4. Loss 计算fp16→fp32 5. 梯度更新fp32→fp16步骤 3 的 Cast 最频繁每层 2 次32 层 64 次但也最容易被融合消除。ops-nn 的fused_linear_act_norm接口把 Linear SiLU LayerNorm 融合后LayerNorm 前后的 Cast 都在 kernel 内部完成。性能数据单独测 Cast 的延迟Atlas 800I A2[1, 4096, 4096] tensor类型连续 tensor非连续 tensorfp16→fp320.018ms0.052msfp32→fp160.018ms0.052msfp16→bf160.020ms0.058msbf16→fp160.020ms0.058ms非连续 tensor 的 Cast 慢 3 倍。如果你在模型里做了 reshape、permute、stride 等操作记得加.contiguous()再做类型转换。Cast 不是性能主角但它无处不在。混合精度训练的 Cast 开销通常占总训练时间的 2-3%看起来不多但全部消除后相当于免费多了一张卡 3% 的算力。方法是走融合算子路径 确保 tensor contiguous。仓库在这里https://atomgit.com/cann/ops-math