Closed
[Documentation|文档反馈]: aclnnBincount 功能说明「存放到 out 的 self[i]+1 位置」与公式 out[self_i] 相矛盾 #3961
liuhanwen8Add on  20 days agoClosed at  4 days ago
liuhanwen8
liuhanwen8
20 days ago Create

一、文档链接 (必填)

https://gitcode.com/cann/ops-math/blob/master/math/bincount/docs/aclnnBincount.md

二、问题文档片段 (必填)

math/bincount/docs/aclnnBincount.md:28:…self[i]每出现一次,则其频率加1…最后存放到out的self[i]+1位置上;因此out大小为(self的最大值+1)与minlength中的最大值。
math/bincount/docs/aclnnBincount.md:35:out[self_i] = out[self_i] + weights_i;:41:out[self_i] = out[self_i] + 1。

现象

:28 的「out 的 self[i]+1 位置」与 :35/:41 的公式 out[self_i] 互斥,实际行为与公式一致:值为 v 的元素把计数累加到 out[v],不是 out[v+1]。实测 self=[0×8, 1, 2]、minlength=4 时 out=[8,1,1,0],与 torch.bincount 完全相同;按 :28 字面「+1 偏移」读法应得 [0,8,1,1],与实测不符。头文件 math/bincount/op_api/aclnn_bincount.h:27-30 写的也是 out[n] = out[n] + 1(n 为 self 在位置 i 上的值)。

读法 out[0] out[1] out[2] out[3] 与实测
实测 aclnnBincount / torch.bincount / 公式 :35 :41 8 1 1 0 一致
:28 字面「out 的 self[i]+1 位置」 0 8 1 1 不一致

期望

算子行为无缺陷,属措辞错::28 应写「最后存放到 out 中下标为 self[i] 的位置」,与 :35/:41 公式及实现一致;同句后面「out 大小为(self 的最大值+1)与 minlength 中的最大值」正是按 0 基下标 self[i] 推出的,也反证 +1 说法不成立。

环境

  • 硬件:Atlas A2 910B2(8 卡;0 号卡 Health=Alarm,使用 1 号卡);CANN:Ascend-cann-910b-ops 9.2.0(V100R001C12SPC001B237),aarch64,g++ 11.4.0;torch 2.12.0+cpu(仅取标杆)
  • 仓库与提交:cann/ops-math 584b2ea6cbaeba684a51a74296d82d4d50ab3b0f(git fetch 后的 origin/master)
  • 复现日期:2026-10-25

复现步骤

  1. 保存下方代码为 repro.cpp
  2. export ASCEND_HOME_PATH=<CANN安装目录>; export ASCEND_OPP_PATH=$ASCEND_HOME_PATH/opp; export ASCEND_RT_VISIBLE_DEVICES=1
  3. g++ -std=c++17 -O2 repro.cpp -o repro -I$ASCEND_HOME_PATH/include -I$ASCEND_HOME_PATH/include/aclnnop -L$ASCEND_HOME_PATH/lib64 -Wl,--allow-shlib-undefined -lascendcl -lopapi -lnnopbase
  4. export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:/usr/local/Ascend/driver/lib64:$LD_LIBRARY_PATH; ./repro
  5. (可选)python3 -c "import torch;print(torch.bincount(torch.tensor([0]*8+[1,2]),minlength=4).tolist())" 复核标杆
  6. 看 NPU == 公式 ? True ; NPU == +1 读法 ? False

复现脚本

// build: g++ -std=c++17 -O2 repro.cpp -o repro -I$ASCEND_HOME_PATH/include -I$ASCEND_HOME_PATH/include/aclnnop \
//        -L$ASCEND_HOME_PATH/lib64 -Wl,--allow-shlib-undefined -lascendcl -lopapi -lnnopbase
#include <cstdio>
#include <cstdint>
#include "acl/acl.h"
#include "aclnnop/aclnn_bincount.h"

int main()
{
    const int64_t vals[10] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2};   // 值 0 出现 8 次
    const int64_t N = 4;                                       // minlength=4 => out 长度 4
    int64_t dim[1] = {10}, odim[1] = {N}, str[1] = {1};
    int64_t outHost[4] = {0};
    void *sd = nullptr, *od = nullptr, *wd = nullptr;
    aclInit(nullptr);
    aclrtSetDevice(0);
    aclrtStream st = nullptr;
    aclrtCreateStream(&st);
    aclrtMalloc(&sd, sizeof(vals), ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMemcpy(sd, sizeof(vals), vals, sizeof(vals), ACL_MEMCPY_HOST_TO_DEVICE);
    aclrtMalloc(&od, sizeof(outHost), ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMemcpy(od, sizeof(outHost), outHost, sizeof(outHost), ACL_MEMCPY_HOST_TO_DEVICE);
    aclTensor* self = aclCreateTensor(dim, 1, ACL_INT64, str, 0, ACL_FORMAT_ND, dim, 1, sd);
    aclTensor* out = aclCreateTensor(odim, 1, ACL_INT64, str, 0, ACL_FORMAT_ND, odim, 1, od);
    uint64_t ws = 0;
    aclOpExecutor* ex = nullptr;
    aclnnStatus r1 = aclnnBincountGetWorkspaceSize(self, nullptr, N, out, &ws, &ex);
    aclnnStatus r2 = r1;
    if (r1 == ACL_SUCCESS) {
        if (ws > 0) { aclrtMalloc(&wd, ws, ACL_MEM_MALLOC_HUGE_FIRST); }
        r2 = aclnnBincount(wd, ws, ex, st);
        aclrtSynchronizeStream(st);
        aclrtMemcpy(outHost, sizeof(outHost), od, sizeof(outHost), ACL_MEMCPY_DEVICE_TO_HOST);
    }
    // 对照组:资料 :35/:41 公式读法;缺陷组:资料 :28 的字面「+1 偏移」读法
    int64_t formula[4] = {0}, plus1[4] = {0};
    for (int i = 0; i < 10; ++i) {
        formula[vals[i]] += 1;                                 // 资料 :35/:41  out[self_i] += 1
        if (vals[i] + 1 < N) { plus1[vals[i] + 1] += 1; }      // 资料 :28 字面「self[i]+1 位置」
    }
    bool eqFormula = true, eqPlus1 = true;
    for (int i = 0; i < N; ++i) {
        eqFormula = eqFormula && (outHost[i] == formula[i]);
        eqPlus1 = eqPlus1 && (outHost[i] == plus1[i]);
    }
    printf("ret_first=%d ret_second=%d\n", (int)r1, (int)r2);
    printf("index             0  1  2  3\n");
    printf("NPU aclnnBincount %2lld %2lld %2lld %2lld  <- 实测\n",
           (long long)outHost[0], (long long)outHost[1], (long long)outHost[2], (long long)outHost[3]);
    printf("公式 :35/:41      %2lld %2lld %2lld %2lld  <- 与 torch.bincount 相同\n",
           (long long)formula[0], (long long)formula[1], (long long)formula[2], (long long)formula[3]);
    printf(":28 +1 偏移读法   %2lld %2lld %2lld %2lld\n",
           (long long)plus1[0], (long long)plus1[1], (long long)plus1[2], (long long)plus1[3]);
    printf("NPU == 公式 ? %s ; NPU == +1 读法 ? %s\n", eqFormula ? "True" : "False", eqPlus1 ? "True" : "False");
    aclDestroyTensor(self);
    aclDestroyTensor(out);
    aclrtFree(sd);
    aclrtFree(od);
    if (wd != nullptr) { aclrtFree(wd); }
    aclrtDestroyStream(st);
    aclrtResetDevice(0);
    aclFinalize();
    return 0;
}

实测输出(ASCEND_OPP_PATH 显式指向本机 CANN 的 opp,1 号卡;末两行为第 5 步 torch 复核):

ret_first=0 ret_second=0
NPU aclnnBincount  8  1  1  0  <- 实测
公式 :35/:41       8  1  1  0  <- 与 torch.bincount 相同
:28 +1 偏移读法    0  8  1  1
NPU == 公式 ? True ; NPU == +1 读法 ? False
torch 2.12.0+cpu / torch.bincount [8, 1, 1, 0]

影响

只影响语义理解、不影响数值:按 :28 写 CPU 参考实现或写读 out 的下游代码时会整体错位一格,还会读 out[max+1](越界一格);排查精度问题时先怀疑算子而不是自己的读法。资料 :361-:364 的示例用 INT64 out 且与公式一致,与 :28 的措辞互相矛盾。

建议

改 math/bincount/docs/aclnnBincount.md:28:把「最后存放到out的self[i]+1位置上」改为「最后存放到out中下标为self[i]的位置」,与 :35/:41 公式、aclnn_bincount.h:27-30 及实现一致。查重:命中的 #3865、#3695、#3861、#3697、#3800 均为 SparseBincount / RaggedBinCount(另一组算子),没有针对 aclnnBincount 功能说明的 issue。

likedislike
liuhanwen8liuhanwen8
20 days ago Added label: documentation
陈思
陈思Collaborators
19 days ago Comments:

您好,感谢指出。按您给出的 584b2ea6cbaeba684a51a74296d82d4d50ab3b0f 核查,文档公式与头文件说明均使用零基下标:值为 v 的元素累加到 out[v],没有额外的下标偏移。

“self[i]+1 位置”容易混淆从 1 开始的序号与从 0 开始的下标,建议明确改为“存放到 out 中下标为 self[i] 的位置”。输出长度中的 max(self)+1 则应保留,因为下标需要覆盖 0 到最大值;您这组输入对应 [8,1,1,0]。本单可按文档措辞歧义跟踪,我们会继续确认说明与公式的对齐方式。

likedislike
陈思陈思Collaborators
19 days ago Assign liu-wei as responsible person
陈思陈思Collaborators
19 days ago Added label: pending-confirmation
陈思陈思Collaborators
19 days ago Removed label: pending-confirmation
陈思陈思Collaborators
9 days ago Assign FelixTang7 as responsible person
陈思陈思Collaborators
9 days ago Removed responsible person liu-wei
weikeweikeCollaborators
5 days ago Associated pull request
CANN-robotCANN-robotCollaborators
4 days ago Close issue
CANN-robotCANN-robotCollaborators
4 days ago changed custom state from 进行中 to 已完成
CANN-robotCANN-robotCollaborators
4 days ago Added label: resolved