以下 8 个 foreach inplace 算子,在就地参数传入非连续 Tensor(stride 与 shape 不匹配的视图)时,计算结果被静默丢弃:
ForeachACosInplace、ForeachAddListInplace、ForeachDivListInplace、ForeachLogInplace、ForeachMulListInplace、ForeachMulScalarInplace、ForeachSubListInplace、ForeachSubScalarInplace
ForeachACosInplace
ForeachAddListInplace
ForeachDivListInplace
ForeachLogInplace
ForeachMulListInplace
ForeachMulScalarInplace
ForeachSubListInplace
ForeachSubScalarInplace
表现是:aclnn 两段式接口均返回 0,不报错、不崩溃,aclrtSynchronizeStream 也正常返回,但用户传入的原始 storage 纹丝不动,读回去还是计算前的输入值。调用方没有任何途径感知失败,比直接报错更危险。
aclrtSynchronizeStream
根因:这 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,所以完全没有症状——这是该问题从算子首版潜伏至今未被发现的原因。
OpDef
Input
Output
x serves as both input and output (no Output declared)
NnopbaseSetRef
NnopbaseGetRefUnContiguousTensors
NnopbaseSetViewCopyExecutor
aclnnViewCopy
NnopbaseGetUnContiguousTensors
aclnnContiguous
快速判据(查装机包头文件即可,无需跑用例):
const aclTensorList *x ← const 且无 Ref 后缀 = 无 ref 配对,有此问题 aclTensorList *xRef ← 非 const 且有 Ref 后缀 = ref 配对存在,正常
影响面:仅影响直接调用 aclnn 接口且传入非连续就地参数的调用方。PyTorch 通路不受影响(连续场景走非 inplace 的 aclnnForeachXxx,非连续场景 PyTorch 自身会回退到逐 Tensor 的手写 aclnnInplaceXxx,后者内部有 Contiguous + ViewCopy)。
aclnnForeachXxx
aclnnInplaceXxx
master
bash build.sh --pkg --ops=<算子名> --soc=ascend950
用「哨兵法」构造一个非连续视图即可复现。以 ForeachLogInplace 为例:
[v0, S, v1, S, v2, S, v3, S]
v*
S
aclCreateTensor
shape=[4]
strides=[2]
storageDims=[8]
aclTensorList
aclnnForeachLogInplaceGetWorkspaceSize
aclnnForeachLogInplace
log(v)
其余 7 个算子同理,把有效值换成各自定义域内的取值即可(ACos 用 [-1,1],list 类算子把第二个输入列表传连续 Tensor)。
ACos
编译探针时需链接 vendor 包的 libcust_opapi.so(内置聚合库里是未改版本),并加 -Wl,--allow-shlib-undefined,运行时配 LD_PRELOAD=<CANN>/lib64/libops_base.so。
libcust_opapi.so
-Wl,--allow-shlib-undefined
LD_PRELOAD=<CANN>/lib64/libops_base.so
注意:MulScalarInplace / SubScalarInplace 的 scalar、AddListInplace / SubListInplace 的 alpha 都是单元素张量,对它们做「非连续」没有鉴别力(stride 不产生位移,必然通过)。真正能暴露问题的只有多元素的 TensorList 就地参数。
MulScalarInplace
SubScalarInplace
scalar
AddListInplace
SubListInplace
alpha
就地参数传入非连续 Tensor 时,计算结果应当按视图的 stride 正确回写到用户原始 storage:视图覆盖到的位置为计算结果,视图未覆盖的位置保持原值不被踩踏。这也是本仓 ScatterMax(Input("var") + Output("var") 同名 ref 配对,签名为 aclTensor *varRef)等算子的既有行为。
ScatterMax
Input("var")
Output("var")
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(...); // 全文再无一处调用
ForeachZeroInplace
ForeachNonFiniteCheckAndUnscale
scatter_add
scatter_max
scatter_min
关联 PR:https://gitcode.com/cann/ops-nn/pull/9498
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,所以完全没有症状——这是该问题从算子首版潜伏至今未被发现的原因。快速判据(查装机包头文件即可,无需跑用例):
影响面:仅影响直接调用 aclnn 接口且传入非连续就地参数的调用方。PyTorch 通路不受影响(连续场景走非 inplace 的
aclnnForeachXxx,非连续场景 PyTorch 自身会回退到逐 Tensor 的手写aclnnInplaceXxx,后者内部有 Contiguous + ViewCopy)。Environment / 环境信息
master分支上述 8 个算子自建立以来的所有版本bash build.sh --pkg --ops=<算子名> --soc=ascend950Steps to reproduce the issue / 重现步骤
用「哨兵法」构造一个非连续视图即可复现。以
ForeachLogInplace为例:[v0, S, v1, S, v2, S, v3, S]交错铺数据,其中v*是有效值(取 1/2/4/8),S是哨兵值 -777;aclCreateTensor建一个shape=[4]、strides=[2]、storageDims=[8]的视图,即只覆盖 storage 的偶数位;aclTensorList,依次调用aclnnForeachLogInplaceGetWorkspaceSize与aclnnForeachLogInplace,然后aclrtSynchronizeStream;log(v),奇数位哨兵保持 -777;其余 7 个算子同理,把有效值换成各自定义域内的取值即可(
ACos用 [-1,1],list 类算子把第二个输入列表传连续 Tensor)。编译探针时需链接 vendor 包的
libcust_opapi.so(内置聚合库里是未改版本),并加-Wl,--allow-shlib-undefined,运行时配LD_PRELOAD=<CANN>/lib64/libops_base.so。Describe the expected behavior / 预期结果
就地参数传入非连续 Tensor 时,计算结果应当按视图的 stride 正确回写到用户原始 storage:视图覆盖到的位置为计算结果,视图未覆盖的位置保持原值不被踩踏。这也是本仓
ScatterMax(Input("var")+Output("var")同名 ref 配对,签名为aclTensor *varRef)等算子的既有行为。Related log / screenshot / 日志 / 截图
在修复前的代码出包上运行上述哨兵探针(8 算子 × dtype 支持面 × 4 种非连续场景 = 67 例),结果为 15 PASS / 52 FAIL,且通过的 15 例恰好是全部「仅纯输入 x2 非连续」的用例(该路径本来就正常),凡就地参数非连续的 52 例全部失败:
失败签名一律是「实得 == 原始输入值」,即 storage 完全没有被写过、结果静默丢弃;同时哨兵位无一被踩,说明也不是越界写坏。
对照生成代码可直接看到回写四件套只有声明没有调用:
Special notes for this issue / 备注
ForeachZeroInplace与ForeachNonFiniteCheckAndUnscale也是同样形态,但这两者同时注册了 Atlas A2/A3 训练系列、Atlas 200I/500 A2 推理系列与 Kirin,且在 A2 侧已有 aclnn 接口,修改等于变更跨代 IR 原型,影响面更大,不在本 Issue 与关联 PR 的范围内,建议单独评估。scatter_add/scatter_max/scatter_min是Input("var")+Output("var")同名配对,签名为aclTensor *varRef,ref 回写正常,无此问题。关联 PR:https://gitcode.com/cann/ops-nn/pull/9498