已关闭
[Bug-Report|缺陷反馈]: 8 个 foreach*Inplace 算子就地参数传非连续 Tensor 时结果被静默丢弃 #5335
TangPC创建于  8月29日关闭于  8月29日
TangPC
TangPC成员
8月29日 创建

Describe the current behavior / 问题描述

以下 8 个 foreach inplace 算子,在就地参数传入非连续 Tensor(stride 与 shape 不匹配的视图)时,计算结果被静默丢弃:

ForeachACosInplace、ForeachAddListInplace、ForeachDivListInplace、ForeachLogInplace、ForeachMulListInplace、ForeachMulScalarInplace、ForeachSubListInplace、ForeachSubScalarInplace

表现是:aclnn 两段式接口均返回 0,不报错、不崩溃,aclrtSynchronizeStream 也正常返回,但用户传入的原始 storage 纹丝不动,读回去还是计算前的输入值。调用方没有任何途径感知失败,比直接报错更危险。

根因:这 8 个算子的 OpDef 只声明了 Input、没有声明 Output(原注释即写明 x serves as both input and output (no Output declared))。opbuild 因此拿不到输出 IR 索引,建不起 input↔output 的 ref 配对,导致生成的 aclnn 代码里回写四件套 NnopbaseSetRef、NnopbaseGetRefUnContiguousTensors、NnopbaseSetViewCopyExecutor、aclnnViewCopy 全部只有 extern 声明、零调用;而输入连续化(NnopbaseGetUnContiguousTensors + aclnnContiguous)照常生成。于是非连续的就地参数被抠进临时连续缓冲,内核往临时缓冲就地写,算完没有任何一段代码把它搬回用户 storage,结果被丢弃。连续场景不产生临时缓冲、内核直接写用户 GM,所以完全没有症状——这是该问题从算子首版潜伏至今未被发现的原因。

快速判据(查装机包头文件即可,无需跑用例):

const aclTensorList *x    ← const 且无 Ref 后缀 = 无 ref 配对,有此问题
aclTensorList       *xRef ← 非 const 且有 Ref 后缀 = ref 配对存在,正常

影响面:仅影响直接调用 aclnn 接口且传入非连续就地参数的调用方。PyTorch 通路不受影响(连续场景走非 inplace 的 aclnnForeachXxx,非连续场景 PyTorch 自身会回退到逐 Tensor 的手写 aclnnInplaceXxx,后者内部有 Contiguous + ViewCopy)。

Environment / 环境信息

  • 芯片:Ascend950PR(Product Name: A310-50-C00MM304A1,Chip Version V100)
  • Driver / Firmware:Software Version 25.7.rc1,Firmware Version 9.0.0.100.200
  • CANN:Ascend-cann-toolkit 9.1.0(innerversion V100R001C25B114)
  • 仓库:cann/ops-nn,问题存在于 master 分支上述 8 个算子自建立以来的所有版本
  • 编译命令:bash build.sh --pkg --ops=<算子名> --soc=ascend950

Steps to reproduce the issue / 重现步骤

用「哨兵法」构造一个非连续视图即可复现。以 ForeachLogInplace 为例:

  1. 在 device 上申请一段 storage,按 [v0, S, v1, S, v2, S, v3, S] 交错铺数据,其中 v* 是有效值(取 1/2/4/8),S 是哨兵值 -777;
  2. 用 aclCreateTensor 建一个 shape=[4]、strides=[2]、storageDims=[8] 的视图,即只覆盖 storage 的偶数位;
  3. 用该 Tensor 建 aclTensorList,依次调用 aclnnForeachLogInplaceGetWorkspaceSize 与 aclnnForeachLogInplace,然后 aclrtSynchronizeStream;
  4. 把整段 storage 拷回 host 检查:
    • 期望:偶数位变为 log(v),奇数位哨兵保持 -777;
    • 实际:偶数位仍是 1/2/4/8(原始输入值),奇数位哨兵保持 -777,两段接口返回值均为 0。

其余 7 个算子同理,把有效值换成各自定义域内的取值即可(ACos 用 [-1,1],list 类算子把第二个输入列表传连续 Tensor)。

编译探针时需链接 vendor 包的 libcust_opapi.so(内置聚合库里是未改版本),并加 -Wl,--allow-shlib-undefined,运行时配 LD_PRELOAD=<CANN>/lib64/libops_base.so。

注意:MulScalarInplace / SubScalarInplace 的 scalar、AddListInplace / SubListInplace 的 alpha 都是单元素张量,对它们做「非连续」没有鉴别力(stride 不产生位移,必然通过)。真正能暴露问题的只有多元素的 TensorList 就地参数。

Describe the expected behavior / 预期结果

就地参数传入非连续 Tensor 时,计算结果应当按视图的 stride 正确回写到用户原始 storage:视图覆盖到的位置为计算结果,视图未覆盖的位置保持原值不被踩踏。这也是本仓 ScatterMax(Input("var") + Output("var") 同名 ref 配对,签名为 aclTensor *varRef)等算子的既有行为。

在修复前的代码出包上运行上述哨兵探针(8 算子 × dtype 支持面 × 4 种非连续场景 = 67 例),结果为 15 PASS / 52 FAIL,且通过的 15 例恰好是全部「仅纯输入 x2 非连续」的用例(该路径本来就正常),凡就地参数非连续的 52 例全部失败:

== foreach*Inplace 非连续支持面实测(哨兵 -777)==
-- ForeachLogInplace
  ForeachLogInplace fp32 S1:仅就地参数NC(3T)  FAIL
    [值错 逻辑idx0(storage0) 期望0 实得1]
    [值错 逻辑idx1(storage2) 期望0.693147 实得2]
    [值错 逻辑idx2(storage4) 期望1.38629 实得4]
    [值错 逻辑idx3(storage6) 期望2.07944 实得8]
-- ForeachMulScalarInplace
  ForeachMulScalarInplace fp32 S1:仅就地参数NC(3T)  FAIL
    [值错 逻辑idx0(storage0) 期望6 实得2]
    [值错 逻辑idx1(storage2) 期望12 实得4]
    [值错 逻辑idx2(storage4) 期望18 实得6]
    [值错 逻辑idx3(storage6) 期望24 实得8]
...
== 合计 PASS=15 FAIL=52 ==

失败签名一律是「实得 == 原始输入值」,即 storage 完全没有被写过、结果静默丢弃;同时哨兵位无一被踩,说明也不是越界写坏。

对照生成代码可直接看到回写四件套只有声明没有调用:

extern aclnnStatus NnopbaseSetRef(void *executor, const size_t inputIrIdx, const size_t outputIrIdx);
extern aclnnStatus NnopbaseGetRefUnContiguousTensors(...);
extern aclnnStatus NnopbaseSetViewCopyExecutor(...);
// 全文再无一处调用

Special notes for this issue / 备注

  • 同类问题共影响 10 个 foreach inplace 算子。除上述 8 个外,ForeachZeroInplace 与 ForeachNonFiniteCheckAndUnscale 也是同样形态,但这两者同时注册了 Atlas A2/A3 训练系列、Atlas 200I/500 A2 推理系列与 Kirin,且在 A2 侧已有 aclnn 接口,修改等于变更跨代 IR 原型,影响面更大,不在本 Issue 与关联 PR 的范围内,建议单独评估。
  • 已排除 scatter 族:scatter_add / scatter_max / scatter_min 是 Input("var") + Output("var") 同名配对,签名为 aclTensor *varRef,ref 回写正常,无此问题。
  • 修复方案与完整验证数据见关联 PR。

关联 PR:https://gitcode.com/cann/ops-nn/pull/9498

likedislike
TangPCTangPC成员
8月29日 添加了label:bug-report
CANN-robotCANN-robot成员
8月29日 关闭了 issue
CANN-robotCANN-robot成员
8月29日 添加了label:resolved