Closed
[Documentation|文档反馈]: aclnnTopkV2 资料声明 sorted 仅支持 true,实测 sorted=false 被接受且结果仍有序(两段返回 0) #3971
liuhanwen8Add on  14 days agoClosed at  10 days ago
liuhanwen8
liuhanwen8
14 days ago Create

现象

aclnnTopkV2 的 sorted=false 被接受(两段返回 0),且输出仍是有序的;而资料明确写「当前该参数仅支持取 True」。

输入 phase1 phase2 row0 values 说明
sorted=true 0 0 9 8 7 对照组
sorted=false 0 0 9 8 7 资料称仅支持 true,实测放行且行为等价于 true

资料 math/topk/docs/aclnnTopkV2.md L128(sorted 使用说明)原文:
True表示输出结果排序,False表示输出结果不排序。注意:当前该参数仅支持取True。

期望

两种收法任选其一,但不能保持现状(声明"仅支持 True"却接受 False 且静默按 True 处理):① 第一段对 sorted=false 返回 161002,与"仅支持取 True"的声明一致;② 或在资料里写明「sorted=false 当前等价于 true」,把这层退化关系说清楚。

环境

  • 硬件:Atlas A2(910B2),npu-smi 26.1.1,aarch64
  • CANN:9.2.0,g++ 11.4.0,glibc 2.35
  • 仓库与提交:cann/ops-math 584b2ea6
  • 复现日期:2026-10-25

复现步骤

  1. 保存下方代码为 repro.cpp
  2. source $ASCEND_HOME_PATH/set_env.sh
  3. g++ -std=c++17 -O2 repro.cpp -o repro -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 -Wl,-rpath,$ASCEND_HOME_PATH/lib64 -Wl,--allow-shlib-undefined -lascendcl -lopapi -lnnopbase
  4. REPRO_DEVICE=1 ./repro(0 号 die 非健康时换健康 die 编号)
  5. 对比两组:sorted=false 与 sorted=true 的返回码与 values 完全相同

复现脚本

// aclnnTopkV2: doc says "sorted 当前仅支持取 True", but sorted=false is accepted
// (both stages return 0) and the result is still sorted.
// Control group: sorted=true -> 0/0.
#include <cstdio>
#include <cstdlib>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_topk.h"

static aclTensor* Mk(std::vector<int64_t> sh, aclDataType dt, void* dev) {
    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(), dev);
}

static int Run(bool sorted, void* self, void* val, void* idx) {
    aclTensor* ts = Mk({4, 8}, ACL_FLOAT, self);
    aclTensor* tv = Mk({4, 3}, ACL_FLOAT, val);
    aclTensor* ti = Mk({4, 3}, ACL_INT64, idx);
    uint64_t ws = 0;
    aclOpExecutor* ex = nullptr;
    int r = (int)aclnnTopkV2GetWorkspaceSize(ts, 3, 1, true, sorted, 0, tv, ti, &ws, &ex);
    printf("  sorted=%-5s -> phase1=%d", sorted ? "true" : "false", r);
    if (r == 0) {
        void* p = nullptr;
        if (ws > 0) aclrtMalloc(&p, ws, ACL_MEM_MALLOC_HUGE_FIRST);
        int r2 = (int)aclnnTopkV2(p, ws, ex, nullptr);
        aclError sr = aclrtSynchronizeStream(nullptr);
        float out[3] = {0, 0, 0};
        aclrtMemcpy(out, 12, val, 12, ACL_MEMCPY_DEVICE_TO_HOST);
        printf("  phase2=%d sync=%d  row0 values=%.0f %.0f %.0f\n", r2, (int)sr,
               out[0], out[1], out[2]);
        if (p) aclrtFree(p);
    } else {
        printf("\n");
    }
    aclDestroyTensor(ts); aclDestroyTensor(tv); aclDestroyTensor(ti);
    return r;
}

int main() {
    int dev = getenv("REPRO_DEVICE") ? atoi(getenv("REPRO_DEVICE")) : 0;
    aclInit(nullptr);
    aclrtSetDevice(dev);

    void *self = nullptr, *val = nullptr, *idx = nullptr;
    aclrtMalloc(&self, 128, ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMalloc(&val, 48, ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMalloc(&idx, 96, ACL_MEM_MALLOC_HUGE_FIRST);
    float host[32];
    for (int r = 0; r < 4; ++r) {
        float row[8] = {9, 3, 7, 3, 1, 8, 2, 5};
        for (int c = 0; c < 8; ++c) host[r * 8 + c] = row[c];
    }
    aclrtMemcpy(self, 128, host, 128, ACL_MEMCPY_HOST_TO_DEVICE);

    printf("[1] control: sorted=true\n");
    Run(true, self, val, idx);
    printf("[2] defect : sorted=false (doc: only true is supported)\n");
    Run(false, self, val, idx);

    aclrtFree(self); aclrtFree(val); aclrtFree(idx);
    aclrtResetDevice(dev);
    aclFinalize();
    return 0;
}

实测输出:

[1] control: sorted=true
  sorted=true  -> phase1=0  phase2=0 sync=0  row0 values=9 8 7
[2] defect : sorted=false (doc: only true is supported)
  sorted=false -> phase1=0  phase2=0 sync=0  row0 values=9 8 7

影响

从 torch.topk(..., sorted=False) 迁过来的代码会按"不排序"的语义使用结果(只取集合、依赖原始出现顺序做后续 gather),实际拿到有序结果;返回码是 0,偏差不会在调用点暴露,只能靠下游数值异常反推。

建议

优先按①在第一段拒绝(math/topk/op_api/aclnn_topk.cpp 的 CheckParamValid 里对 sorted == false 返回 161002),与同文件已有 dim/k 越界检查放在一起;若产品上确需支持 sorted=false,按②改资料并写明退化语义。

likedislike
liuhanwen8liuhanwen8
14 days ago Added label: bug-report
陈思
陈思Collaborators
13 days ago Comments:

您好,感谢提供 sorted 两种取值的对照。资料“仅支持 True”与您观察到 false 被接受之间,确实需要明确支持口径;但这组结果还不能证明 false 被全局忽略或始终等价于 true。

语义与源码核查

“无需保证排序”不等于“输出必须无序”,也不等于“按原始出现顺序输出”。对迁移自 PyTorch 的场景,官方 topk 说明只在 sorted=True 时保证结果有序,并明确等值元素的索引不保证稳定。因此不能依赖 sorted=False 保持原始顺序;下游应使用返回的 values/indices 配对关系,而非自行假设顺序。

按您给出的 584b2ea6cbaeba684a51a74296d82d4d50ab3b0f 核查,L0 Topk确实将 sorted 作为属性传给 TopKV2,并未统一改成 true。本例 A2、FLOAT、小尾轴选择常规 AiCore 路径;其他类型和大形状还有 RadixTopK 分流,其中超 INT32 索引范围的选择条件明确依赖 !sorted。所以不能把一组小形状的有序输出推广成所有后端都按 true 处理。

当前固定版本仓内未定位到报告提及的公开 aclnn_topk.cpp 包装,商用库入口与具体 kernel 对 false 的行为仍需核对;以上不代表已独立设备复验。

对齐方向

建议先确认各支持平台、类型和算法的 sorted 契约。若采用“不要求输出有序”的语义,应在资料中明确结果仍可能有序,且不承诺输入顺序或等值索引稳定性;不宜写成 false 永远等价于 true。若某些配置确实只支持 true,应限定对应范围,再评估入口拒绝及兼容影响,而不是直接全局拒绝 false。

回归应检查不同尾轴长度、k、largest、dtype 和算法分支的完整 values/indices 对应关系;当前脚本仅展示首行 values,尚不能验证索引及所有形状的顺序行为。我们会继续确认支持范围与顺序说明的对齐方式。

likedislike
陈思陈思Collaborators
13 days ago Assign chen_0715 as responsible person
陈思陈思Collaborators
13 days ago Added label: pending-confirmation
陈思陈思Collaborators
13 days ago Removed label: pending-confirmation
Nice tryNice tryCollaborators
12 days ago Assign Nice_try as responsible person
Nice tryNice tryCollaborators
11 days ago Associated pull request
CANN-robotCANN-robotCollaborators
10 days ago Close issue
CANN-robotCANN-robotCollaborators
10 days ago Added label: resolved