已合并
【fix】: PowScalar性能问题优化 #1795
【fix】: PowScalar性能问题优化 #1795
已合并
liyuewei创建于 8月21日
共 3 个文件变更+92-2
@@ -280,6 +280,41 @@ constexpr __aicore__ static inline T AfInfinity() {
280 return T();280 return T();
281}281}
282 282 
283+inline __aicore__ bool IsIntegerWithinUint32Magnitude(float value) {
284+ constexpr uint32_t kExponentMask = 0x7F800000U;
285+ constexpr uint32_t kFractionMask = 0x007FFFFFU;
286+ constexpr uint32_t kExponentBias = 127U;
287+ constexpr uint32_t kFractionBits = 23U;
288+ constexpr uint32_t kUint32Exponent = 32U;
289+ 
290+ const uint32_t bits = GetScalarValueByBitCode<float, uint32_t>(value);
291+ const uint32_t exponent = (bits & kExponentMask) >> kFractionBits;
292+ const uint32_t fraction = bits & kFractionMask;
293+ 
294+ // NaN and infinity are not integers.
295+ if (exponent == 0xFFU) {
296+ return false;
297+ }
298+ 
299+ // Ignore the sign bit and limit the absolute value to the uint32 range.
300+ if (exponent >= kExponentBias + kUint32Exponent) {
301+ return false;
302+ }
303+ 
304+ // Zero and non-zero values with an absolute value less than one.
305+ if (exponent < kExponentBias) {
306+ return exponent == 0U && fraction == 0U;
307+ }
308+ 
309+ const uint32_t integer_bits = exponent - kExponentBias;
310+ if (integer_bits >= kFractionBits) {
311+ return true;
312+ }
313+ 
314+ const uint32_t fractional_mask = (1U << (kFractionBits - integer_bits)) - 1U;
315+ return (fraction & fractional_mask) == 0U;
316+}
317+ 
283static constexpr float ROUND_TO_NEAREST_INT_BIAS = 0.5f;318static constexpr float ROUND_TO_NEAREST_INT_BIAS = 0.5f;
284template <typename T>319template <typename T>
285inline __aicore__ uint32_t ConvertToUint32(T value) {320inline __aicore__ uint32_t ConvertToUint32(T value) {
@@ -47,6 +47,27 @@ TEST(TestUtilsApi, Floor_ShouldReturnInt_WhenNumIsUnsignedInt) {
47 ASSERT_EQ(Floor(0U), 0);47 ASSERT_EQ(Floor(0U), 0);
48}48}
49 49 
50+TEST(TestUtilsApi, IsInteger_ShouldCheckExactFloatValue) {
51+ EXPECT_TRUE(IsIntegerWithinUint32Magnitude(0.0f));
52+ EXPECT_TRUE(IsIntegerWithinUint32Magnitude(-0.0f));
53+ EXPECT_TRUE(IsIntegerWithinUint32Magnitude(2.0f));
54+ EXPECT_TRUE(IsIntegerWithinUint32Magnitude(-2.0f));
55+ EXPECT_TRUE(IsIntegerWithinUint32Magnitude(16777216.0f));
56+ EXPECT_TRUE(IsIntegerWithinUint32Magnitude(4294967040.0f));
57+ EXPECT_TRUE(IsIntegerWithinUint32Magnitude(-4294967040.0f));
58+ 
59+ EXPECT_FALSE(IsIntegerWithinUint32Magnitude(0.5f));
60+ EXPECT_FALSE(IsIntegerWithinUint32Magnitude(-2.25f));
61+ EXPECT_FALSE(IsIntegerWithinUint32Magnitude(123456.75f));
62+ EXPECT_FALSE(IsIntegerWithinUint32Magnitude(4294967296.0f));
63+ EXPECT_FALSE(IsIntegerWithinUint32Magnitude(-4294967296.0f));
64+}
65+ 
66+TEST(TestUtilsApi, IsInteger_ShouldRejectNanAndInfinity) {
67+ EXPECT_FALSE(IsIntegerWithinUint32Magnitude(AfInfinity<float>()));
68+ EXPECT_FALSE(IsIntegerWithinUint32Magnitude(GetScalarValueByBitCode<uint32_t, float>(0x7FC00000U)));
69+}
70+ 
50TEST(TestUtilsApi, TestFindNearestPower2) {71TEST(TestUtilsApi, TestFindNearestPower2) {
51 uint64_t n1 = KernelUtils::FindNearestPower2(0);72 uint64_t n1 = KernelUtils::FindNearestPower2(0);
52 ASSERT_EQ(n1, 0);73 ASSERT_EQ(n1, 0);
@@ -59,7 +59,24 @@ inline __aicore__ void Pow(const AscendC::LocalTensor<T> &dst, const AscendC::Lo
59 return;59 return;
60 }60 }
61 } else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, bfloat16_t>) {61 } else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, bfloat16_t>) {
62- Power<T, false, pow_config>(dst, src1, src2, tmp_buf, calCount);62+ if (IsIntegerWithinUint32Magnitude(src2)) {
63+ const bool is_negative = src2 < 0.0f;
64+ const float abs_src2 = is_negative ? -src2 : src2;
65+ uint32_t src_u32 = static_cast<uint32_t>(abs_src2);
66+ Duplicate(dst, static_cast<float>(1.0), calCount);
67+ while (src_u32 > 0) {
68+ if (src_u32 & 0x01) {
69+ Mul(dst, dst, src1, calCount);
70+ }
71+ src_u32 >>= 1;
72+ Mul(src1, src1, src1, calCount);
73+ }
atomgit-botatomgit-bot
atomgit-botatomgit-bot8月21日

🟠 High Priority

changed line → affected behavior/contract → failure mode → suggested fix

本 PR 在 autofuse/v35/ascendc/api_regbase/pow.h 的标量指数 Pow 重载中新增了整数指数的快速路径(第 62-73 行)。第 63 行计算了 is_negative = src2 < 0.0f,第 64-65 行取绝对值 abs_src2 并转为 uint32_t src_u32,随后第 67-73 行循环只用 src_u32 做快速幂累乘,is_negative 变量被算出后从未使用,循环结束后直接 return(第 77 行),没有任何求倒数/回退逻辑。

新增的 IsIntegerWithinUint32Magnitude 明确忽略符号位(utils.h 第 299-302 行注释 "Ignore the sign bit"),因此负整数指数(如 -2.0f、-3.0f,对 bfloat16_t 还包括 -1.0f、-0.5f 因第 35/50 行特殊分支仅覆盖 float/half)会命中该快速路径。例如 Pow(dst, src1, -2.0f, ...) 应得 x^(-2)=1/x²,实际得到 x²;-1.0f 的 bfloat16 场景应得 1/x,实际得到 x。

失败模式:任何调用方传入负整数标量指数时,kernel 静默产出错误数值,无任何告警,属本次变更引入的回归(改动前该分支直接走 Power 通用实现,结果正确)。指数为 0.5/-0.5 等非整数不会命中此路径(IsIntegerWithinUint32Magnitude(±0.5) 返回 false),故该错误仅影响负整数指数,触发条件明确。

建议:在进入快速路径前增加非负判断,负指数回退到 Power 通用路径,例如将第 62 行改为 if (src2 >= 0.0f && IsIntegerWithinUint32Magnitude(src2)),并删除未使用的 is_negative/abs_src2 逻辑(或改为循环后对 dst 求倒数)。

likedislike
不准确?
atomgit-botatomgit-bot8月21日

🟠 High Priority

变更行:pow.h 62-73 行新增的整数指数快速幂分支。

影响/契约:第 63 行 const bool is_negative = src2 < 0.0f; 计算了符号,但该变量从未被使用;第 64-73 行直接用 |n|(abs_src2 → src_u32)做平方累乘,循环结果为 x^|n|。

失败模式:当 src2 为负数且幅值在 uint32 范围内(如 -2.0f、-3.0f、-4.0f 等;-1.0f/-0.5f 已被前面的分支拦截)时,IsIntegerWithinUint32Magnitude(-2.0f) 返回 true 进入快速路径,输出 x² 而非数学上正确的 1/x²,与基线 Power(exp2/log2 路径)语义不一致,静默产生错误数值。该问题对 T=float 与 T=bfloat16_t 均存在。

修复:仅对非负整数指数启用快速路径,其余(含所有负指数)回退到 Power<T, false, pow_config>。最小改动:把进入条件改为 IsIntegerWithinUint32Magnitude(src2) && !(src2 < 0.0f),并删除未使用的 is_negative/abs_src2。

建议:快速路径只应处理非负整数指数:将条件改为 if (IsIntegerWithinUint32Magnitude(src2) && !(src2 < 0.0f)),删除 is_negative/abs_src2 两行,直接 uint32_t src_u32 = static_cast<uint32_t>(src2);;负指数统一走 Power 基线路径。

改动建议
73
+ if (IsIntegerWithinUint32Magnitude(src2) && !(src2 < 0.0f)) {
73
- }
74
+ uint32_t src_u32 = static_cast<uint32_t>(src2);
75
+ Duplicate(dst, static_cast<float>(1.0), calCount);
应用建议
likedislike
不准确?
atomgit-botatomgit-bot8月21日

🟡 Medium Priority

变更行:pow.h 62-73 行新增的整数指数快速路径(本次 PR 的核心变更)。

影响/契约:本次改动触及 Pow 的核心计算路径——新增按位快速幂分支(改变计算结果来源)、原地改写输入张量 src1、并对负指数引入了语义变更;但同目录的 test_pow.cpp(UT 中唯一调用该 Pow 重载的用例文件)未做任何修改,新增测试只覆盖了 utils.h 的 IsInteger 函数。

失败模式:上述两个真实缺陷(负整数指数输出 x^|n| 而非 1/x^|n|;src1 被原地平方污染、dst==src1 时结果恒为 1.0)没有任何用例覆盖——只要新增一个 Pow(x, -2.0f) 或 Pow 原地调用用例即可暴露,当前改动即便存在这些回归也能通过 UT。这是高风险路径(复杂分支、数值语义、输入张量副作用)缺少关键测试。

修复:在 test_pow.cpp 中为 float/bfloat16 补充快速路径用例,至少覆盖:(1) 正整数指数(如 4、5、大指数)与数学期望/基线 Power 对比;(2) 负整数指数(如 -2、-3)验证 1/x^|n|;(3) 调用后复用 src1 以及 dst==src1 别名场景,防止输入张量被污染。

建议:在 test_pow.cpp 中补充对新增快速路径的用例:正/负整数指数结果验证、调用后 src1 复用与 dst==src1 别名场景,覆盖负数指数与输入污染这两类回归。

likedislike
不准确?
74+ if (is_negative) {
75+ Reciprocal(dst, dst, calCount);
76+ }
77+ } else {
78+ Power<T, false, pow_config>(dst, src1, src2, tmp_buf, calCount);
79+ }
63 return;80 return;
64 }81 }
65 Power(dst, src1, src2, tmp_buf, calCount);82 Power(dst, src1, src2, tmp_buf, calCount);
@@ -122,7 +139,24 @@ inline __aicore__ void Pow(const AscendC::LocalTensor<T> &dst, const T &src1, co
122 need_power = false;139 need_power = false;
123 }140 }
124 } else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, bfloat16_t>) {141 } else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, bfloat16_t>) {
125- Power<T, false, pow_config>(dst_buf, src1_buf, src2, tmp_buf, block_cnt);142+ if (IsIntegerWithinUint32Magnitude(src2)) {
143+ const bool is_negative = src2 < 0.0f;
144+ const float abs_src2 = is_negative ? -src2 : src2;
145+ uint32_t src_u32 = static_cast<uint32_t>(abs_src2);
146+ Duplicate(dst_buf, static_cast<float>(1.0), block_cnt);
147+ while (src_u32 > 0) {
148+ if (src_u32 & 0x01) {
149+ Mul(dst_buf, dst_buf, src1_buf, block_cnt);
150+ }
151+ src_u32 >>= 1;
152+ Mul(src1_buf, src1_buf, src1_buf, block_cnt);
153+ }
154+ if (is_negative) {
155+ Reciprocal(dst_buf, dst_buf, block_cnt);
156+ }
157+ } else {
158+ Power<T, false, pow_config>(dst_buf, src1_buf, src2, tmp_buf, block_cnt);
159+ }
126 need_power = false;160 need_power = false;
127 }161 }
128 if (need_power) {162 if (need_power) {