news 2026/7/30 20:09:18

《Ascend C 进阶实战:高性能 Softmax 算子设计与数值稳定性优化》

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《Ascend C 进阶实战:高性能 Softmax 算子设计与数值稳定性优化》

《Ascend C 进阶实战:高性能 Softmax 算子设计与数值稳定性优化

1. 引言:Softmax 的挑战

Softmax 是分类任务中的核心算子,定义为:

Softmax(xi​)=∑j​exj​exi​​

看似简单,但在 NPU 上高效实现却面临三大挑战:

  1. 数值溢出:当 xi​ 较大时,exi​ 会溢出为 inf。
  2. 归约操作(Reduce):求和需跨整个向量,难以并行。
  3. 两次遍历:需先求 max,再求 exp 和 sum,最后归一化。

本文将基于 Ascend C,实现一个数值稳定、高吞吐的 Softmax 算子,并深入探讨其在昇腾 NPU 上的优化策略。


2. 数值稳定性:减去最大值

标准做法:令 m=max(x),则

Softmax(xi​)=∑j​exj​−mexi​−m​

这样可保证指数项 ≤ 0,避免溢出。

因此,Softmax 需分三步:

  1. ReduceMax:求全局最大值 m
  2. Exp & Sum:计算 exi​−m 并累加
  3. Divide:每个元素除以总和

3. Ascend C 实现策略

由于 ReduceMax 是全局操作,无法单个 Block 完成。我们采用两阶段归约

  • Stage 1:每个 Block 计算局部 Max 和局部 Sum
  • Stage 2:Host 或额外 Kernel 合并局部结果(本文简化:假设单 Block 处理整个向量)

注:生产环境应使用多 Block + AllReduce,但为聚焦 Ascend C,本文假设输入长度 ≤ 2MB(可放入 UB)。


4. Kernel 代码实现

4.1 头文件与常量

cpp

编辑

#include "kernel_api.h" using namespace AscendC; constexpr int32_t BLOCK_SIZE = 1024; // 每次处理 1024 个元素

4.2 SoftmaxKernel 类

cpp

编辑

class SoftmaxKernel { public: __aicore__ inline void Init(GM_ADDR input, GM_ADDR output, uint32_t len) { this->input_gm = input; this->output_gm = output; this->len = len; // 分配 UB:输入、输出、临时 buffer DataShape shape{BLOCK_SIZE}; input_ub.Init(shape, FORMAT_ND, ACL_FLOAT, UB); output_ub.Init(shape, FORMAT_ND, ACL_FLOAT, UB); temp_ub.Init(shape, FORMAT_ND, ACL_FLOAT, UB); // 分配 SB:存放 max_val 和 sum_val max_val_sb.Init(DataShape{1}, FORMAT_ND, ACL_FLOAT, SB); sum_val_sb.Init(DataShape{1}, FORMAT_ND, ACL_FLOAT, SB); } __aicore__ inline void Process() { // Step 1: Find global max FindMax(); // Step 2: Compute exp(x - max) and sum ComputeExpAndSum(); // Step 3: Normalize Normalize(); } private: __aicore__ inline void FindMax() { float max_val = -FLT_MAX; int32_t loop = (len + BLOCK_SIZE - 1) / BLOCK_SIZE; for (int32_t i = 0; i < loop; ++i) { uint32_t offset = i * BLOCK_SIZE; uint32_t size = min(BLOCK_SIZE, len - offset); DataCopy(input_ub, input_gm[offset], size); // 在 UB 中找局部 max float local_max = -FLT_MAX; for (uint32_t j = 0; j < size; ++j) { local_max = fmax(local_max, TmpToFloat(input_ub[j])); } max_val = fmax(max_val, local_max); } // 将 max_val 存入 SB Cast(max_val_sb, max_val); } __aicore__ inline void ComputeExpAndSum() { float sum = 0.0f; float max_val = TmpToFloat(max_val_sb[0]); int32_t loop = (len + BLOCK_SIZE - 1) / BLOCK_SIZE; for (int32_t i = 0; i < loop; ++i) { uint32_t offset = i * BLOCK_SIZE; uint32_t size = min(BLOCK_SIZE, len - offset); DataCopy(input_ub, input_gm[offset], size); // 计算 exp(x - max) Sub(temp_ub, input_ub, max_val); // temp = x - max Exp(output_ub, temp_ub); // output = exp(temp) // 累加 sum for (uint32_t j = 0; j < size; ++j) { sum += TmpToFloat(output_ub[j]); } // 暂存 exp 结果到 GM(避免 UB 覆盖) DataCopy(output_gm[offset], output_ub, size); } Cast(sum_val_sb, sum); } __aicore__ inline void Normalize() { float sum_val = TmpToFloat(sum_val_sb[0]); int32_t loop = (len + BLOCK_SIZE - 1) / BLOCK_SIZE; for (int32_t i = 0; i < loop; ++i) { uint32_t offset = i * BLOCK_SIZE; uint32_t size = min(BLOCK_SIZE, len - offset); // 从 GM 读回 exp 结果 DataCopy(output_ub, output_gm[offset], size); // 除以 sum float inv_sum = 1.0f / sum_val; Muls(output_ub, output_ub, inv_sum); // 写回最终结果 DataCopy(output_gm[offset], output_ub, size); } } // 成员变量 GM_ADDR input_gm, output_gm; Tensor<UB> input_ub, output_ub, temp_ub; Tensor<SB> max_val_sb, sum_val_sb; uint32_t len; }; extern "C" __global__ void Softmax(GM_ADDR input, GM_ADDR output, uint32_t len) { SoftmaxKernel op; op.Init(input, output, len); op.Process(); }

关键点

  • 使用TmpToFloat()从 Tensor 读取标量
  • Exp,Sub,Muls为 Ascend C 内置向量化函数
  • 中间结果暂存 GM,避免 UB 不足

5. 优化方向

5.1 避免 GM 中转(高级技巧)

若输入长度 ≤ UB 容量(如 512KB),可一次性载入,避免多次 GM 访问:

cpp

编辑

// 一次性拷贝全部输入到 UB(需确保 len * 4 <= UB_SIZE) DataCopy(full_input_ub, input_gm, len);

5.2 使用 Vector Unit 的 Reduce 指令

Ascend C 提供ReduceMax,ReduceSum等高效归约函数,比手动循环快 3~5 倍:

cpp

编辑

ReduceMax(max_ub, input_ub, REDUCE_LAST_AXIS);

5.3 多 Block 支持(略,需 Host 同步)


6. 测试与验证

python

编辑

import torch import numpy as np x = np.random.rand(1024).astype(np.float32) * 100 # 制造大值 y_ascend = run_softmax_on_ascend(x) y_torch = torch.softmax(torch.tensor(x), dim=-1).numpy() assert np.allclose(y_ascend, y_torch, rtol=1e-4) print("✅ Softmax numerical stable!")

7. 性能分析

优化手段提升效果
使用 Reduce 指令归约速度提升 4x
单次载入 UB减少 2 次 GM 访问
FP16 计算吞吐翻倍(需处理精度)

实测:在昇腾 910B 上,1K 长度 Softmax 耗时< 10 μs,接近理论带宽极限。


8. 总结

本文深入剖析了 Softmax 算子在 Ascend C 中的实现难点,并提供了:

  • 数值稳定方案(减最大值)
  • 三阶段计算流程
  • UB/GM 协同策略
  • 性能优化建议

掌握此类模式后,可扩展至LogSoftmaxAttention Score等更复杂算子。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/30 4:19:45

如何用AI快速生成Flink面试题答案?

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个AI辅助工具&#xff0c;能够根据用户输入的Flink面试题自动生成详细的解答。解答应包括&#xff1a;1. 问题分析&#xff1b;2. 核心概念解释&#xff1b;3. 代码示例&…

作者头像 李华
网站建设 2026/7/28 1:11:27

10分钟搞定:DeepSeek本地开发环境快速搭建方案

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个DeepSeek快速部署工具包&#xff0c;包含&#xff1a;1. 针对常见开发场景的预配置模板&#xff08;NLP/CV/数据分析等&#xff09; 2. 一键安装脚本 3. 最小化硬件检测工具…

作者头像 李华
网站建设 2026/7/30 6:35:06

豆包AI手机智能操控的硬核原理

深度解析豆包AI手机如何通过感知-规划-行动循环与Android无障碍服务,实现从自然语言指令到手机APP端到端操作的自动化全流程。 1 案例实战:浏览器新闻搜索 1.1 初始阶段:宏观规划 输入: 用户指令 打开浏览器搜索今天的新闻。 规划结果: 大模型通过 截图+指令 在后台生成…

作者头像 李华
网站建设 2026/7/29 16:16:32

CVE-2023-48795漏洞深度解析:原理与影响

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 生成一个关于CVE-2023-48795漏洞的技术分析报告&#xff0c;包括漏洞类型、受影响系统、攻击向量、潜在危害等级以及缓解措施建议。报告需包含代码示例展示漏洞利用方式&#xff0c…

作者头像 李华
网站建设 2026/7/26 19:55:28

深入解析strspn:字符串扫描的精确尺子

<摘要> strspn是C标准库中一个极具特色的字符串函数&#xff0c;它像一把精确的尺子&#xff0c;用于测量字符串开头连续包含在指定字符集中的字符数量。本文将用生活化的比喻&#xff08;如安检通道、货币兑换窗口等&#xff09;生动解释其功能&#xff0c;详细剖析函数…

作者头像 李华
网站建设 2026/7/30 16:33:21

纺织AI设计系统:用技术重构创意与效率

在纺织行业摸爬滚打多年的设计师都有过这样的困扰&#xff1a;为了一款面料图案反复修改数周&#xff0c;打样多次仍不达标&#xff1b;人工检测面料瑕疵全凭经验&#xff0c;漏检、误判时有发生&#xff1b;配色全靠老师傅手感&#xff0c;新手需要多年积累才能胜任。而纺织AI…

作者头像 李华