Closed
[Documentation|文档反馈]: aclnnBincount 功能说明「存放到 out 的 self[i]+1 位置」与公式 out[self_i] 相矛盾 #3961
liuhanwen8Add on 20 days agoClosed at 4 days ago
20 days ago Added label: documentation
陈思
19 days ago Comments:
19 days ago Comments:
19 days ago Assign liu-wei as responsible person
19 days ago Added label: pending-confirmation
19 days ago Removed label: pending-confirmation
9 days ago Assign FelixTang7 as responsible person
9 days ago Removed responsible person liu-wei
5 days ago Associated pull request
4 days ago Close issue
4 days ago changed custom state from 进行中 to 已完成
4 days ago Added label: resolved


一、文档链接 (必填)
https://gitcode.com/cann/ops-math/blob/master/math/bincount/docs/aclnnBincount.md
二、问题文档片段 (必填)
现象
: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 上的值)。期望
算子行为无缺陷,属措辞错::28 应写「最后存放到 out 中下标为 self[i] 的位置」,与 :35/:41 公式及实现一致;同句后面「out 大小为(self 的最大值+1)与 minlength 中的最大值」正是按 0 基下标 self[i] 推出的,也反证 +1 说法不成立。
环境
584b2ea6cbaeba684a51a74296d82d4d50ab3b0f(git fetch 后的 origin/master)复现步骤
repro.cppexport ASCEND_HOME_PATH=<CANN安装目录>; export ASCEND_OPP_PATH=$ASCEND_HOME_PATH/opp; export ASCEND_RT_VISIBLE_DEVICES=1g++ -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 -lnnopbaseexport LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:/usr/local/Ascend/driver/lib64:$LD_LIBRARY_PATH; ./repropython3 -c "import torch;print(torch.bincount(torch.tensor([0]*8+[1,2]),minlength=4).tolist())"复核标杆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 复核):影响
只影响语义理解、不影响数值:按 :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。