ops-nn 里那些算子到底怎么选?我按场景捋了一遍

发布时间:2026/5/22 13:28:53

ops-nn 里那些算子到底怎么选?我按场景捋了一遍 之前有个同事问我“ops-nn 和 ops-math 有什么区别激活函数放哪个仓库”我答不上来。翻了一圈文档才发现CANN 的算子仓库划分逻辑不是按功能来的是按用途来的。ops-math 存基础数学运算ops-nn 存神经网络专用的算子。但有些算子两边都有比如 GELU——ops-math 里有ops-nn 里也有。我花了两天把 ops-nn 的算子按场景捋了一遍这篇整理出来免得你再走弯路。ops-nn 的定位神经网络专用算子ops-nn 是 CANN 核心算子仓库之一存放的是神经网络训练和推理中高频使用的算子。和 ops-math 的边界仓库定位典型算子ops-math通用数学运算Add/Mul/ReduceSum/Log/Expops-nn神经网络专用MatMul/Activation/Conv/BatchNorm/Softmaxops-blas线性代数专用GEMM/GEMVops-transformer大模型专用FlashAttention/MoE重叠区域GELU、Softmax 这类算子在 ops-math 和 ops-nn 都有。区别是 ops-nn 的版本做了神经网络场景的优化比如和 Conv/BatchNorm 的融合ops-math 的版本更通用。简单判断如果你的算子只用在神经网络前向/反向传播里用 ops-nn 的版本。如果通用计算也要用用 ops-math 的版本。按场景选算子场景1模型推理——激活函数怎么选ops-nn 里最常见的激活函数算子适用场景显存占用计算速度ReLUCNN 模型最低最快GELUTransformer 模型中等中等SiLU/SwishLLaMA 等 LLM中等中等FastGELUGELU 的近似实现最低最快关键区别GELU 和 FastGELU。GELU 的精确公式是x * Φ(x)其中 Φ 是标准正态分布的累积分布函数。计算时要用到 erf 函数开销比较大。FastGELU 用一个多项式近似替代 erf精度损失很小但速度快很多。import torch import time def bench_activation(fn, x, warmup5, runs100): # 预热 for _ in range(warmup): _ fn(x) torch.npu.synchronize() times [] for _ in range(runs): start time.time() _ fn(x) torch.npu.synchronize() times.append(time.time() - start) return sum(times) / len(times) * 1000 # 测试数据 x torch.randn(1, 4096, 4096, dtypetorch.float16).npu() # GELU精确版 gelu_time bench_activation(torch.nn.functional.gelu, x) # FastGELU近似版ops-nn 里的实现 # PyTorch 里通过近似模式调用 fast_gelu_time bench_activation( lambda x: torch.nn.functional.gelu(x, approximatetanh), x ) # SiLU silu_time bench_activation(torch.nn.functional.silu, x) # ReLU relu_time bench_activation(torch.nn.functional.relu, x) print(f{算子:12} {延迟(ms):12} {相对ReLU:10}) print(- * 34) print(f{ReLU:12} {relu_time:.3f}{ *6} {1.00x:10}) print(f{GELU:12} {gelu_time:.3f}{ *6} {gelu_time/relu_time:.2f}x) print(f{FastGELU:12} {fast_gelu_time:.3f}{ *6} {fast_gelu_time/relu_time:.2f}x) print(f{SiLU:12} {silu_time:.3f}{ *6} {silu_time/relu_time:.2f}x)实测结果算子延迟(ms)相对 ReLUReLU0.0821.00xFastGELU0.1191.45xSiLU0.1431.74xGELU0.1982.41x结论如果你的模型用 GELU换成 FastGELU 几乎不损失精度但能快 40%。LLaMA 系列用的 SiLU 没法替换但性能也还好。场景2训练加速——BatchNorm 和 Conv 怎么融合ops-nn 最重要的优化之一是Conv BatchNorm Activation 三合一融合。推理时BatchNorm 的参数可以提前融合到 Conv 的权重里不需要单独计算。这是老套路了但 ops-nn 做了更进一步融合后的算子在 Cube 单元上一次完成不需要中间结果落回显存。import torch import torch.nn as nn import time # 未融合版本 class ConvBnRelu(nn.Module): def __init__(self, in_ch, out_ch, kernel_size3): super().__init__() self.conv nn.Conv2d(in_ch, out_ch, kernel_size, padding1, biasFalse) self.bn nn.BatchNorm2d(out_ch) self.relu nn.ReLU(inplaceTrue) def forward(self, x): x self.conv(x) x self.bn(x) x self.relu(x) return x # 融合版本推理专用 class FusedConvBnRelu(nn.Module): def __init__(self, conv_bn_relu_module): super().__init__() # 把 BN 的参数融合到 Conv 权重里 conv conv_bn_relu_module.conv bn conv_bn_relu_module.bn # 融合公式W_fused W * (gamma / sqrt(var eps)) # b_fused beta - gamma * mean / sqrt(var eps) gamma bn.weight.data beta bn.bias.data mean bn.running_mean.data var bn.running_var.data eps bn.eps scale gamma / torch.sqrt(var eps) self.weight nn.Parameter(conv.weight.data * scale.reshape(-1, 1, 1, 1)) self.bias nn.Parameter(beta - mean * scale) self.relu nn.ReLU(inplaceTrue) def forward(self, x): # 融合后只需要一次 Conv ReLU x torch.nn.functional.conv2d(x, self.weight, self.bias, padding1) x self.relu(x) return x # 性能对比 model_unfused ConvBnRelu(64, 128).eval().npu() model_fused FusedConvBnRelu(model_unfused).eval().npu() x torch.randn(1, 64, 224, 224).npu() def benchmark(model, x, warmup10, runs50): for _ in range(warmup): _ model(x) torch.npu.synchronize() times [] for _ in range(runs): start time.time() _ model(x) torch.npu.synchronize() times.append(time.time() - start) return sum(times) / len(times) * 1000 unfused_time benchmark(model_unfused, x) fused_time benchmark(model_fused, x) print(f未融合: {unfused_time:.3f}ms) print(f融合后: {fused_time:.3f}ms) print(f提升: {(1 - fused_time/unfused_time)*100:.1f}%) # 验证精度 with torch.no_grad(): out_unfused model_unfused(x) out_fused model_fused(x) max_diff (out_unfused - out_fused).abs().max().item() print(f最大精度差异: {max_diff:.6f}) # 应该 1e-4实测结果配置延迟(ms)提升比例Conv BN ReLU未融合1.82ms基线Conv BN ReLU融合后1.24ms32%最大精度差异0.000031可忽略场景3显存优化——Softmax 的 FP16 陷阱Softmax 看起来简单但在 FP16 下有个经典陷阱指数溢出。import torch # FP16 Softmax 溢出演示 x torch.tensor([[1.0, 2.0, 100.0]], dtypetorch.float16).npu() # 直接 softmaxFP16 下会溢出 try: result torch.softmax(x, dim-1) print(fFP16 softmax: {result}) # 可能出现 NaN 或不正确的分布 except Exception as e: print(fError: {e}) # 正确做法先减最大值再 softmaxops-nn 的实现已经内置了这个 x_safe x - x.max(dim-1, keepdimTrue).values result_safe torch.softmax(x_safe.float(), dim-1).half() print(fSafe softmax: {result_safe})ops-nn 的 Softmax 实现已经内置了数值稳定处理不需要你手动减最大值。但如果你自己写 kernel必须注意这个问题。在昇腾 NPU 上ops-nn 的 Softmax 还做了一个额外优化分块计算。长序列的 Softmax 不需要把整个向量加载到本地内存可以分块求 max、分块求 exp 再归一化。这个优化和 FlashAttention 的分块思想一样。# 长序列 Softmax 性能测试 import time def bench_softmax(seq_len, warmup5, runs50): x torch.randn(1, 32, seq_len, dtypetorch.float16).npu() for _ in range(warmup): _ torch.softmax(x, dim-1) torch.npu.synchronize() times [] for _ in range(runs): start time.time() _ torch.softmax(x, dim-1) torch.npu.synchronize() times.append(time.time() - start) return sum(times) / len(times) * 1000 seq_lengths [512, 1024, 2048, 4096, 8192] print(f{序列长度:12} {延迟(ms):12}) print(- * 24) for s in seq_lengths: t bench_softmax(s) print(f{s:12} {t:.3f})实测结果序列长度延迟(ms)5120.01510240.02820480.05340960.10181920.196延迟和序列长度基本线性关系说明分块优化生效了。如果没有分块8192 的延迟应该是 512 的 256 倍N²实际只有 13 倍。完整代码ops-nn 算子选型速查把上面的测试串起来做一个自动选型脚本 ops-nn 算子选型工具 根据模型类型和场景推荐算子组合 import torch def recommend_ops(model_type, scenario): 根据模型类型和场景推荐算子 recommendations { (transformer, inference): { activation: FastGELU比 GELU 快 40%精度损失 0.1%, softmax: ops-nn Softmax内置分块 数值稳定, normalization: LayerNorm推理时权重可预融合, attention: ops-transformer FlashAttention不是 ops-nn但必须提, tip: GELU 换 FastGELU 是最划算的单项优化 }, (transformer, training): { activation: GELU训练需要精确梯度不要用 FastGELU, softmax: ops-nn Softmax, normalization: LayerNorm, attention: FlashAttention训练和推理都建议用, tip: 训练时精度优先推理时速度优先 }, (cnn, inference): { activation: ReLU最快CNN 不需要 GELU, softmax: ops-nn Softmax, normalization: BatchNorm权重预融合到 Conv 里, conv_bn_relu: 融合算子省 30% 延迟, tip: BatchNo

相关新闻