Pull Request已成功合入, 合并人@CANN-robot
(感谢 kangjiaming 的贡献)变更摘要
此 PR 修复了 ExpintKernel 在 Ascend950 平台上的多个数值计算缺陷,主要包括:指数函数溢出防护、区间计算逻辑重构、边界条件判定修正,以及非 float32 类型的输出钳位。核心改动集中在 Compute 函数的区间有理逼近实现和 ApplyBoundaries 的边界处理上,将原本通过辅助函数 ComputeRationalInterval/ComputeInterval1 实现的分段计算展开为内联的 Select-merge 模式,并引入 EXP_CLAMP 常量防止 exp(x) 在 float32 下溢出。
主要改动
-
引入
EXP_CLAMP钳位防止 exp 溢出: 将原NEAR_ZERO_THRESHOLD常量替换为EXP_CLAMP = 88.0f,在Compute入口处将 x 钳位至 88.0f,避免exp(x)在 float32 下产生 +inf;同时在ProcessFp16Bf16中对half/bfloat16结果分别钳位到 65504.0f 和约 3.39e+38f,防止Cast时溢出。 -
重写
Compute区间有理逼近逻辑: 将原本通过ComputeRationalInterval(区间 6-2)和ComputeInterval1(区间 1)实现的 7 段分段计算,全部展开为内联代码,统一采用DataCopy保存旧结果 +Compare/Select按 x 区间条件合并的模式,区间 1 新增使用Ln+ 级数展开计算Ei(x) = γ + ln(x) + x·P1(x)/Q1(x)。 -
修正边界条件判定:
ApplyBoundaries中将原有的NEAR_ZERO_THRESHOLD容差比较改为精确的CMPMODE::EQ与 0.0f 比较判定 x==0 的情况,返回-inf;新增对正无穷的精确判定返回+inf;x<0 返回NaN。 -
结果缓冲区变更:
Compute中不再使用resultBuf,改为直接使用yLocal作为结果缓冲区(注释说明resultBuf在 Ascend950 上存在问题);新增initBuf用于CopyIn中对 float 类型输入数据的初始化。 -
符号可见性修复: 在
expint_tiling_arch35.cpp中为IMPL_OP_OPTILING(Expint)添加__attribute__((visibility("default"))),确保符号正确导出。


代码审查
I've completed a thorough review of both changed files. Let me now close with the summary.
审查总结
| 文件 | 状态 |
|---|---|
math/expint/op_host/arch35/expint_tiling_arch35.cpp |
存在 P1 问题(BUFFER_NUM_FP32 未更新) |
math/expint/op_kernel/expint_kernel.h |
存在 P2 问题(resultBuf 浪费 UB)+ P3 问题(死代码未清理) |
问题统计: P1 × 1, P2 × 1, P3 × 1
整体风险评估: 本次变更的主要风险来自 kernel 侧新增 initBuf 后未同步更新 tiling 侧的 BUFFER_NUM_FP32,可能导致 fp32 路径 UB 超分。虽然超分量较小(约 0.4%),但在 UB 紧张的平台配置下可能导致运行时分配失败或内存踩踏。建议在合入前修复此问题。其余两个问题(未使用的 resultBuf 和死代码)严重度较低,可在后续清理。
| 类型 | 数量 |
|---|---|
| 🔴 阻塞 | 1 |
| 🟡 建议 | 1 |
⛔ 需要修改


The MR can not be merged, because of CodeReview discussion not resolved
If you want to solve this problem, you can click here to do it in the FAQs.


描述
1. PR 概述
本 PR 修复了 expint(指数积分 Ei(x))算子在 Ascend950 上的多个计算正确性问题。
2. PR 解决的问题
2.1 exp(x) 浮点溢出
exp(x)在 float32 下溢出为 inf,导致后续计算全部失效。EXP_CLAMP = 88.0f,将 x 钳位(clamp)到 88.0。因为exp(88) ≈ 1.65e38 < FLT_MAX(3.4e38),而exp(89) ≈ 4.49e38 > FLT_MAX,所以 88 是保证 exp(x) 不溢出的最大整数。2.2 对齐填充导致的除零
CMP_ALIGN = 64),尾部不足部分填充 padding。padding 区域的值为未初始化内存,1/x产生 inf/nan 污染结果。initBuf,在CopyIn中先将输入 buffer 预填充为1.0f(保证1/1.0 = 1.0不产生 inf),再执行DataCopyPad覆盖有效区域。2.3 近零阈值判断不精确
NEAR_ZERO_THRESHOLD = 1e-10f通过Abs(x) <= 1e-10判断 x 是否为零,会将1e-11等小非零值误判为零并输出-inf。x == 0.0f(CMPMODE::EQ),仅在 x 真正为零时返回-inf。2.4 resultBuf 在 Ascend950 上的硬件兼容性问题
resultBuf作为结果缓冲区在 Ascend950 上存在硬件问题。yLocal(输出队列张量)作为结果缓冲区,通过引用LocalTensor<float>& result = yLocal实现。2.5 FP16/BF16 Cast 溢出
Cast会产生 inf。2.6 符号可见性
IMPL_OP_OPTILING(Expint)未导出符号,可能导致动态链接时找不到算子注册。__attribute__((visibility("default")))。3. 代码检视发现的问题及修复
3.1 魔鬼数字(Magic Numbers)
ProcessFp16Bf1665504.0fFP16_MAX_VALUEProcessFp16Bf163.38953138927157e+38fBF16_MAX_VALUECompute(Interval 6)64.0fINTERVAL_BOUND_64Compute(Interval 5)32.0fINTERVAL_BOUND_32Compute(Interval 4)16.0fINTERVAL_BOUND_16Compute(Interval 3)8.0fINTERVAL_BOUND_8Compute(Interval 2)4.0fINTERVAL_BOUND_4Compute(Interval 1)2.0fINTERVAL_BOUND_2新增常量定义:
FP16_MAX_VALUE、BF16_MAX_VALUEINTERVAL_BOUND_2/4/8/16/32/643.2 死代码(Dead Code)
PR 将
ComputeRationalInterval、ComputeInterval1、ApplyBoundaries三个函数的逻辑内联到Compute中,但未删除原函数定义和声明,导致 96 行死代码残留。修复:删除三个函数的声明和定义。
3.3 未使用的 resultBuf
resultBuf在Compute中已被yLocal引用替代,但成员变量声明和InitBuffer调用仍然保留,浪费 UB 内存。修复:
resultBuf成员变量pipe.InitBuffer(resultBuf, ...)调用BUFFER_NUM_FP16从 10 更新为 9(减少一个 float buffer 计数)3.4 initBuf 无条件分配
initBuf仅在T = float路径使用(CopyIn中的if constexpr),FP16/BF16 路径不需要,但原先无条件分配。修复:将
pipe.InitBuffer(initBuf, ...)包裹在if constexpr (std::is_same_v<T, float>)中。3.5 变量作用域过大
CopyIn中alignedCount在if constexpr块外声明,但仅在该块内使用。修复:将
alignedCount声明移入if constexpr块内。3.6 中文注释
PR 中新增了中文注释,与代码库英文注释风格不一致。
修复:将以下中文注释翻译为英文:
// EXP_CLAMP: 钳位 x 到 88.0f,防止 exp(x) 在 float32 下溢出// x == 0 → -inf (精确判断)// 钳位 float32 结果到目标 dtype 范围,防止 Cast 溢出为 inf3.7 声明与定义参数名不一致
Compute函数声明中参数名为xLocal,定义中为xFp32,不一致。修复:统一为
xFp32。4. 验证
4.1 编译验证
bash build.sh --ops=expint --soc=ascend950 -j$(nproc)结果:FP16、FP32、BF16 三种 dtype 的 kernel binary 全部编译成功。
4.2 功能验证
结果:在 Ascend950 NPU 上运行通过。
输入:4x4 全 2.0 的 float32 张量。
输出:全部为
4.954234,与 Ei(2) 的理论值4.95423435600189在 float32 精度下一致。5. 修改文件清单
math/expint/op_kernel/expint_kernel.hmath/expint/op_host/arch35/expint_tiling_arch35.cppBUFFER_NUM_FP16从 10 更新为 9净减少 68 行代码。
关联的Issue
https://gitcode.com/cann/ops-math/issues/2383
测试
文档更新
类型标签