Closed
[Bug-Report|缺陷反馈]: aclnnMoeTokenPermuteGrad 标量 numTopk 负数未校验(-1 静默当 1,-3 崩溃 507035) #6672
liuhanwen8Add on 15 days agoClosed at 8 days ago
15 days ago Added label: bug-report
周云飞
15 days ago Comments:
15 days ago Comments:
您好,感谢提供完整的复现脚本和对照结果。这里需要区分标量参数校验与张量内容校验:numTopk 是 int64_t 标量,可以在主机侧直接检查,不需要读取设备上的张量数据。
问题分析
接口参数检查函数 CheckParams 目前只检查空指针和数据类型,其参数中没有 numTopk,因此该函数没有承担这一标量的范围校验。
您提供的 A2 910B2、CANN 9.2.0 测试结果表明,-1 和 -3 均通过了第一段接口,后者在同步时返回 507035。您指出的 -1 转为 1 的分支,以及其他负数参与 token 数计算的路径,是重要的定位线索。当前可确认的 safeDiv 实现仅处理除数为零的情况,不会拒绝负除数;但负值如何继续影响 SetBlockDim 并触发异常,仍需结合后续调用路径确认。
另有一处返回码需要区分:实测 numTopk = 0 是第二段返回 361001,并不是第一段返回您期望的 ACLNN_ERR_PARAM_INVALID / 161002,二者不宜视为同一行为。
处理方向
一个可行的调整方向是,在第一段接口的参数检查中传入并校验 numTopk,使非法值在进入 tiling 和设备执行前返回参数错误。具体下界需要先明确 -1 是否属于需要保留的接口语义:若不支持哨兵值,可按 [1, 512] 检查并同步补全文档;若确需保留 -1,则应明确其实际含义、限制其他负数,并评估直接拒绝 -1 对现有调用的兼容影响,不能将“按 1 处理”等同于“自动推断”。
目前提供的环境、脚本和输出已足够作为继续定位的依据,无需重复补充。我们会继续确认负值处理路径及 -1 的接口语义。


周云飞
14 days ago Comments:
14 days ago Comments:
/assign @gcw_oeok7yxb


14 days ago Assign gcw_oeok7yxb as responsible person
10 days ago Associated pull request
8 days ago Close issue
8 days ago Added label: resolved
现象
aclnnMoeTokenPermuteGrad的numTopk是int64_t标量参数(不是 tensor 取值),资料只写了上界
numTopk <= 512,未写下界。实测在形状完全合法的前提下只改这个标量:numTopk = -1不只是「没拒」,还被 tiling 静默改写成 1:numTopk = -3不走这条分支,safeDiv(32, -3)得负tokensNum,SetBlockDim拿到负值 → 核异常。环境
A2 910B2;CANN 9.2.0;
cann/ops-transformermaster92d0f9c2。期望
numTopk是标量参数,host 侧一次比较即可校验,没有性能理由不做。建议:
numTopk < 1时返回ACLNN_ERR_PARAM_INVALID / 161002(与numTopk = 0现状一致);(
-1被当成「自动推断」这类约定若不写进资料,就只能靠读源码猜)。复现步骤
r.cpp。g++ -std=c++17 r.cpp -o r -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 -lascendcl -lnnopbase -lopapi./r 0复现脚本
// aclnnMoeTokenPermuteGrad numTopk 标量非法值复核(编译/运行见「复现步骤」) #include <cstdio> #include <cstdlib> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_moe_token_permute_grad.h" static aclTensor *mk(const std::vector<int64_t> &sh, aclDataType dt, void *dv) { std::vector<int64_t> st(sh.size(), 1); for (int i = (int)sh.size() - 2; i >= 0; --i) st[i] = sh[i + 1] * st[i + 1]; return aclCreateTensor(sh.data(), sh.size(), dt, st.data(), 0, ACL_FORMAT_ND, sh.data(), sh.size(), dv); } int main(int argc, char **argv) { aclInit(nullptr); aclrtSetDevice(argc > 1 ? atoi(argv[1]) : 0); aclrtStream s = nullptr; aclrtCreateStream(&s); // 形状固定(等价 example 的 tokens=8, topK=4):sortedIndices dim0 = 32, hidden = 16 const int TOTAL = 32, H = 16; std::vector<int64_t> gs{TOTAL, H}, is{TOTAL}, os{TOTAL, H}; size_t bg = (size_t)TOTAL * H * 4, bi = (size_t)TOTAL * 4; void *dg, *di, *dout; aclrtMalloc(&dg, bg, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(&di, bi, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(&dout, bg, ACL_MEM_MALLOC_HUGE_FIRST); std::vector<float> hg(TOTAL * H, 1.f); std::vector<int> hi(TOTAL); for (int i = 0; i < TOTAL; ++i) hi[i] = i; // 合法排列 aclrtMemcpy(dg, bg, hg.data(), bg, ACL_MEMCPY_HOST_TO_DEVICE); aclrtMemcpy(di, bi, hi.data(), bi, ACL_MEMCPY_HOST_TO_DEVICE); aclTensor *tg = mk(gs, ACL_FLOAT, dg), *ti = mk(is, ACL_INT32, di); aclTensor *to = mk(os, ACL_FLOAT, dout); const long long ks[5] = {4, 1, 0, -1, -3}; for (int k = 0; k < 5; ++k) { uint64_t ws = 0; aclOpExecutor *ex = nullptr; auto r1 = aclnnMoeTokenPermuteGradGetWorkspaceSize(tg, ti, ks[k], false, to, &ws, &ex); void *wp = nullptr; if (ws > 0) aclrtMalloc(&wp, ws, ACL_MEM_MALLOC_HUGE_FIRST); auto r2 = r1 == 0 ? aclnnMoeTokenPermuteGrad(wp, ws, ex, s) : -999; auto r3 = r1 == 0 ? aclrtSynchronizeStream(s) : -999; printf("numTopk=%-3lld GWS=%-6d Exec=%-6d Sync=%-6d\n", ks[k], (int)r1, (int)r2, (int)r3); } return 0; }实测输出(第一段全部返回 0,即从不拦):
影响
numTopk = -1静默改语义:tiling 改写成 1,调用方以为传了「自动推断」,实际按
topK = 1计算,无任何提示 —— 静默错值风险;numTopk = -3崩溃:tokensNum为负 →SetBlockDim负值 → vector core exception,污染同进程后续调用;
numTopk常由上游配置传入,写错一个负值即踩到。建议
op_api/aclnn_moe_token_permute_grad.cpp的CheckParams增加numTopk >= 1(标量校验零成本,放在 tiling 之前);
-1确要作为「自动推断 topK」的合法语义,必须 ① 写进资料、② 只认
-1这一个哨兵值,-3等其它负数一律拒绝;aclnnMoeTokenPermuteGrad.md:114,244的「numTopk <= 512」补上明确下界。备注
numTopk是int64_t标量,host 侧一次if即可,不存在打断 vector 流水的问题。阴性对照:
numTopk = 0被正确拒绝(361001),说明该接口确有标量校验通道,只是漏了负数分支。