Closed
[Bug-Report|缺陷反馈]: aclnnVar 在 unbiased=true 且归约轴元素数为 1 时首段返回 561103,同仓 aclnnVarCorrection 同场景返回 NAN #4178
liuhanwen8Add on  12 days agoClosed at  7 days ago
liuhanwen8
liuhanwen8
12 days ago Create

一、问题描述

aclnnVar 对资料允许的合法输入(self 任意合法 shape、dim 任意合法取值、unbiased 无附加限制)在
unbiased=true 且归约轴元素数恰为 1 时,第一段 aclnnVarGetWorkspaceSize 直接返回 561103
(ACLNN_ERR_INNER,内部错误),而不是按方差语义返回 nan。

关键对照:同一份代码里的兄弟接口 aclnnVarCorrection 在完全等价的输入
(correction=1、归约轴元素数 1)下正常返回 0 且输出 nan —— 因为它在 aclnn_var.cpp:301-318
有一段 shapeProd <= correction → 填 NAN / INFINITY 的早退分支,而 aclnnVar 没有。

plog 原文(CANN 自身打印,非推测):

op[ReduceStdV2Update], input_shape cannot be less than or equal to correction,
  input_shape_element=1, correction=1.[FUNC:Tiling4ReduceStdFunc][FILE:reduce_std_func.cc][LINE:138]
Failed to execute operator aclnnVar_4_ReduceStdV2Update. Reason: Failed to execute tiling.

即 aclnnVar(unbiased=true) 把 unbiased 直接当 correction=1 传给 ReduceStdV2Update
(aclnn_var.cpp:224),而该内核的 tiling 明确拒绝 input_shape <= correction;由于 aclnnVar
没有像 aclnnVarCorrection 那样提前拦下这个退化情形,tiling 失败被 l0op 冒泡成 561103。

影响面(同一轮 1000 条用例实测,非抽样猜测):

用例集合 条数 结果
unbiased=true 且归约轴元素数 = 1 76 76/76 全部 561103
其余全部合法用例 924 924/924 PASS(数值在容差内)

判据是集合完全等价:ERROR 集合与 {unbiased=true ∧ N_red=1} 逐条相等(无多、无少)。
归约轴元素数=1 在真实模型里很常见(batch=1、seq=1、单通道、归一化维=1),一旦命中即整网报错。

二、环境信息

项 值
硬件 Ascend 910B3(Atlas A2 训练系列),8 卡
CANN 26.1.1(/usr/local/Ascend/ascend-toolkit)
框架 torch 2.12.0+cpu / torch_npu 2.12.0.post3
算子仓 cann/ops-math master,math/reduce_var(ref_commit 72d787648)
资料 math/reduce_var/docs/aclnnVar.md(389 行,sha256 960025a5…)

三、重现步骤

  1. 保存下面的 C++ 探针为 var_n1.cpp(A 子例=被测点,D 子例=aclnnVarCorrection 对照);
  2. 编译:g++ -std=c++17 var_n1.cpp -o var_n1 -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 -lascendcl -lnnopbase -lopapi;
  3. 运行 ./var_n1 A(被测)与 ./var_n1 D(对照),同一份输入 self=[1]、dim=nullptr、keepdim=true。
#include <cstdio>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_var.h"

static aclTensor* Mk(float v, void** addr) {
    std::vector<int64_t> sh{1}, st{1};
    aclrtMalloc(addr, 4, ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMemcpy(*addr, 4, &v, 4, ACL_MEMCPY_HOST_TO_DEVICE);
    return aclCreateTensor(sh.data(), 1, ACL_FLOAT, st.data(), 0, ACL_FORMAT_ND,
                           sh.data(), 1, *addr);
}

int main(int argc, char** argv) {
    bool corr = (argc > 1 && argv[1][0] == 'D');   // D = VarCorrection 对照
    aclInit(nullptr); aclrtSetDevice(0);
    aclrtStream s = nullptr; aclrtCreateStream(&s);
    void *sa = nullptr, *oa = nullptr;
    float v = 7.0f, o = -12345.0f;
    aclTensor* self = Mk(v, &sa);                  // self = [1]
    aclTensor* out  = Mk(o, &oa);                  // out  = [1](keepdim=true)
    uint64_t ws = 0; aclOpExecutor* e = nullptr;
    aclnnStatus r1 = corr
        ? aclnnVarCorrectionGetWorkspaceSize(self, nullptr, 1, true, out, &ws, &e)
        : aclnnVarGetWorkspaceSize(self, nullptr, /*unbiased=*/true, true, out, &ws, &e);
    printf("ret1=%d\n", (int)r1);
    if (r1 == 0) {
        void* wsp = nullptr;
        if (ws > 0) aclrtMalloc(&wsp, ws, ACL_MEM_MALLOC_HUGE_FIRST);
        aclnnStatus r2 = corr ? aclnnVarCorrection(wsp, ws, e, s)
                              : aclnnVar(wsp, ws, e, s);
        aclrtSynchronizeStream(s);
        aclrtMemcpy(&o, 4, oa, 4, ACL_MEMCPY_DEVICE_TO_HOST);
        printf("ret2=%d\nout=%f\n", (int)r2, o);
    }
    return 0;
}

实测输出(910B3,CANN 26.1.1):

$ ./var_n1 A
ret1=561103

$ ./var_n1 D
ret1=0
ret2=0
out=nan

控制变量矩阵(每行只改一个变量,其余同 A):

子例 接口 self dim unbiased/correction ret1 输出
A aclnnVar [1] nullptr unbiased=true 561103 —
B aclnnVar [1] nullptr unbiased=false 0 0(正确)
C aclnnVar [2] nullptr unbiased=true 0 0.5(正确)
D aclnnVarCorrection [1] nullptr correction=1 0 nan(正确)
E aclnnVar [1,3] [0] unbiased=true 561103 —

B/C 通过说明探针构造与绑定无误;D 通过说明不是内核不支持,而是 aclnnVar 少了一段退化分支。

四、预期结果

  1. aclnnVar(self=[1], dim=nullptr, unbiased=true, keepdim=true) 应返回 0,out = nan
    (与 torch.var(x, dim=None, correction=1) 的语义一致;torch.var 在该输入下返回 nan,
    aclnnVarCorrection 已按此实现);
  2. 资料未对 unbiased=true 附加任何 shape 限制,因此当前行为属于「合法输入被拒 + 错误码为内部错误」双重不符。

修复方向(与同文件既有写法对齐,aclnn_var.cpp:301-310 可直接复用):

// aclnnVarGetWorkspaceSize 中,dimArray 解析完成之后、Contiguous/Cast 之前
int64_t shapeProd = CalcShapeProd(self, dimArray);   // 该函数本文件已有(:129-138)
if (shapeProd <= (unbiased ? 1 : 0)) {               // 即 N - correction <= 0
    ret = CheckFillScalarShapeStdAndVar(out, NAN, uniqueExecutor.get());
    *workspaceSize = uniqueExecutor->GetWorkspaceSize();
    uniqueExecutor.ReleaseTo(executor);
    return ret;
}

说明:shapeProd == 0(空 tensor)已由前面的 IsEmpty() 分支覆盖,这里只需补 N - correction <= 0。

备注

  • 查重:按 aclnnVar / 方差 / ReduceStdV2Update / 561103 在 cann/ops-math 全量翻页检索
    (≥3000 条),未发现同现象单据。#2565(closed)处理的是「空 tensor / corner case 路径
    workspaceSize 被误写为 0」,最终结果是 workspace 不足,不是本单的「缺退化分支 → tiling 报错」;
    #4172 是 aclnnVarMean 的 bf16 问题,接口与现象都不同。#3987 是 aclnnVar 文档链接错误。
  • 为什么不是精度问题:76 条全部在第一段就返回非 0,第二段未执行,没有进入数值比对环节。
  • 补充证据:同轮 error-scenario 线的基线恰好落在 self=bf16[1]|dim=null|unbiased=true,
    该基线自身返回 561103,导致该线 6 条「与资料一致」的判据一并降级为 MATCH_UNVERIFIED ——
    这是同一缺陷的第二个受害面(它把别的判据的基线也污染了)。
likedislike
陈思
陈思Collaborators
12 days ago Comments:

您好,感谢反馈!

问题分析

核对 cann/ops-math 固定版本 8c1f633e3679,接口资料没有对 unbiased=true 增加归约元素数必须大于 1 的限制。这里需要按归约轴的元素数 N 判断,self=[1,3]、dim=[0] 同样属于 N=1,不能仅检查输入总元素数。

aclnn_var.cpp 中,aclnnVar 在空张量和参数处理后,没有对 unbiased=true、N=1 提前构建 NaN 输出;910B 所在的非 RegBase 分支仍进入均值、广播及 ReduceStdV2Update 组合链。您提供的日志显示,目标环境的 tiling 按 correction=1 处理无偏估计并拒绝 N=1;对应的 L0 包装在任务构造失败后返回空结果,上层将其映射为 ACLNN_ERR_INNER_NULLPTR(561103),因此失败可以出现在第一段接口,而非数值比对阶段。

同文件的 aclnnVarCorrection 已有 shapeProd==1 && shapeProd<=correction 时填充 NaN 的分支,可在 correction=1 时绕过上述组合计算。这说明两接口对等价边界的处理存在缺口;该对照并不代表底层 kernel 原生支持这一边界。当前确认的是固定版本源码差异,报告中 910B3、CANN 26.1.1 的设备结果尚未由我们独立复测;RegBase 的 ReduceVar 路径也需分别看待。

处理方向

一个可行的方案是在 aclnnVar 完成参数检查、空张量处理及归约轴解析后,为非 RegBase 路径的 unbiased=true、N=1 补充 NaN 输出处理,参考已有 VarCorrection 的填充方式,同时保留填充返回状态、实际 workspace 和 executor 的正常交付。

这一分支需按归约轴乘积覆盖标量、全归约及局部归约,并兼容 keepdim、输出类型和非连续输出;unbiased=false、N=1 的零方差以及现有空张量处理需保持原有语义。我们会继续跟踪这一退化边界的处理差异。

likedislike
陈思陈思Collaborators
12 days ago Assign chen_0715 as responsible person
陈思陈思Collaborators
12 days ago Added label: pending-confirmation
CchenyifengCollaborators
11 days ago Assign m0_55003149 as responsible person
CchenyifengCollaborators
11 days ago Removed responsible person chen_0715
weikeweikeCollaborators
8 days ago Associated pull request
CANN-robotCANN-robotCollaborators
7 days ago Close issue
CANN-robotCANN-robotCollaborators
7 days ago Added label: resolved