已关闭
[Bug] top_k_top_p_sample 对 k==0 缺少下界守卫导致设备异常 #4521
Agent-Bug-Hunter创建于 8月3日关闭于 4 天前
8月4日 将 chenxingyu18 设为负责人
yuning_chen
8月4日 评论:
8月4日 评论:
您好,感谢反馈,问题已收到,当前 @chenxingyu18 正在跟踪处理。


8月6日 将 sunchun 设为负责人,移除负责人 chenxingyu18
4 天前 关闭了 issue
4 天前 添加了label:resolved
摘要
aclnnTopKTopPSample(top_k_top_p_sample)文档写明 topK 有效范围1≤k≤min(voc,1024),超出应跳过 topK。kernel 守卫仅判断temp > topKMax(上界),k==0误入 classic topK,随后(kCount-1)*2无符号下溢,在rowLen>8192时设备异常(本机 Sync 507035)。Ascend910B3 真机 exit 42。本地 draft,未远程提交。环境
e16ef626bb3acann-ops-nn-cann_scan_repro_linux-*.run→$ASCEND_HOME_PATH/vendors/cann_scan_repro_nn为什么认为这是问题:期望 vs 实际
输入(与复现用例一致)
期望行为
实际行为(aclnn / 源码编译安装路径)
对比结论
根因
op_kernel/top_k_top_p_sample.h:上界守卫遗漏k>0;siblingtop_k_top_p_sample_v2已正确使用k>0 && k<=min(...)。Anti-pattern check (AP-001): 910B 配置;无 host k 下界。control-scalar,非 P-011。
为何确认跑的是源码编译内核(非 built-in)
source $ASCEND_HOME_PATH/vendors/cann_scan_repro_nn/bin/set_env.bash,ASCEND_CUSTOM_OPP_PATH指向该 vendor。op_api/op_impl,而非仅依赖 CANN built-in 同名算子(见repro_manifest.json中built_from_source=true)。完整复现过程
占位符:
$ASCEND_HOME_PATH/$OPS_NN/$REPRO_ROOT。步骤 1:环境使能
source <CANN安装目录>/ascend-toolkit/set_env.sh步骤 2:源码编译单算子包
cd "$OPS_NN" bash build.sh --pkg --soc=ascend910b --ops=top_k_top_p_sample \ --vendor_name=cann_scan_repro --no_force -j8成功标志:生成
$OPS_NN/build_out/cann-ops-nn-cann_scan_repro_linux-*.run步骤 3:安装并切换到自定义包
bash $OPS_NN/build_out/cann-ops-nn-cann_scan_repro_linux-*.run \ --quiet --install-path="$ASCEND_HOME_PATH" source "$ASCEND_HOME_PATH/vendors/cann_scan_repro_nn/bin/set_env.bash" echo "ASCEND_CUSTOM_OPP_PATH=$ASCEND_CUSTOM_OPP_PATH"步骤 4:放置复现源码与执行脚本
(本 draft 目录内已含上述文件,可直接作为
$REPRO_ROOT。)步骤 5:编译并运行
cd "$REPRO_ROOT" chmod +x build_and_run_repro.sh bash build_and_run_repro.sh --vendor cann_scan_repro --src test_aclnn_top_k_top_p_sample_k0.cpp # 退出码 42 = BUG_REPRODUCED成功复现标志:进程退出码 42。
实际运行摘录
复现用例源码
文件名:
test_aclnn_top_k_top_p_sample_k0.cpp/** * IDX-TKPS-001: mirror official example dtypes (bf16 logits/topP), trigger k=0. */ #include <cstdio> #include <cstdint> #include <vector> #include <cstring> #include "acl/acl.h" #include "aclnnop/aclnn_top_k_top_p_sample.h" #define LOG_PRINT(message, ...) do { printf(message, ##__VA_ARGS__); } while (0) static int64_t GetShapeSize(const std::vector<int64_t>& shape) { int64_t n = 1; for (auto i : shape) n *= i; return n; } template <typename T> static int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size = GetShapeSize(shape) * sizeof(T); auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); if (ret) return (int)ret; ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); if (ret) return (int)ret; std::vector<int64_t> strides(shape.size(), 1); for (int64_t i = (int64_t)shape.size() - 2; i >= 0; i--) strides[i] = shape[i + 1] * strides[i + 1]; *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } // pack fp32 -> bf16 (truncate mantissa) static uint16_t f32_to_bf16(float f) { uint32_t u; memcpy(&u, &f, 4); return (uint16_t)(u >> 16); } struct CaseR { int ws = -1, launch = -1, sync = -1; int64_t idx = -999; }; static CaseR Run(const char* label, int32_t kVal, int64_t B, int64_t V, aclrtStream stream) { CaseR cr; LOG_PRINT("\n===== %s k=%d B=%ld V=%ld =====\n", label, kVal, (long)B, (long)V); std::vector<int64_t> logitsShape = {B, V}; std::vector<int64_t> topKPShape = {B}; std::vector<uint16_t> logitsHost(B * V); for (int64_t i = 0; i < B * V; i++) logitsHost[i] = f32_to_bf16(0.01f); // max at 1234 for batch 0 logitsHost[1234] = f32_to_bf16(50.0f); std::vector<int32_t> topKHost(B, kVal); std::vector<uint16_t> topPHost(B, f32_to_bf16(1.0f)); std::vector<float> qHost(B * V, 1.0f); std::vector<int64_t> idxHost(B, -1); std::vector<float> selHost(B * V, 0.f); void *logitsDev = nullptr, *topKDev = nullptr, *topPDev = nullptr, *qDev = nullptr; void *idxDev = nullptr, *selDev = nullptr; aclTensor *logits = nullptr, *topK = nullptr, *topP = nullptr, *q = nullptr; aclTensor *idxOut = nullptr, *selOut = nullptr; if (CreateAclTensor(logitsHost, logitsShape, &logitsDev, ACL_BF16, &logits)) return cr; if (CreateAclTensor(topKHost, topKPShape, &topKDev, ACL_INT32, &topK)) return cr; if (CreateAclTensor(topPHost, topKPShape, &topPDev, ACL_BF16, &topP)) return cr; if (CreateAclTensor(qHost, logitsShape, &qDev, ACL_FLOAT, &q)) return cr; if (CreateAclTensor(idxHost, topKPShape, &idxDev, ACL_INT64, &idxOut)) return cr; if (CreateAclTensor(selHost, logitsShape, &selDev, ACL_FLOAT, &selOut)) return cr; float eps = 1e-8f; // header: bool isNeedLogits; example used int64 — try false bool isNeedLogits = false; int64_t topKGuess = 32; uint64_t ws = 0; aclOpExecutor* ex = nullptr; cr.ws = aclnnTopKTopPSampleGetWorkspaceSize(logits, topK, topP, q, eps, isNeedLogits, topKGuess, idxOut, selOut, &ws, &ex); LOG_PRINT(" ws=%d size=%lu\n", cr.ws, (unsigned long)ws); if (cr.ws == 0) { void* waddr = nullptr; if (ws) aclrtMalloc(&waddr, ws, ACL_MEM_MALLOC_HUGE_FIRST); cr.launch = aclnnTopKTopPSample(waddr, ws, ex, stream); cr.sync = aclrtSynchronizeStream(stream); LOG_PRINT(" launch=%d sync=%d\n", cr.launch, cr.sync); if (cr.launch == 0 && cr.sync == 0) { aclrtMemcpy(&cr.idx, sizeof(cr.idx), idxDev, sizeof(cr.idx), ACL_MEMCPY_DEVICE_TO_HOST); LOG_PRINT(" idx=%ld\n", (long)cr.idx); } if (waddr) aclrtFree(waddr); } aclDestroyTensor(logits); aclDestroyTensor(topK); aclDestroyTensor(topP); aclDestroyTensor(q); aclDestroyTensor(idxOut); aclDestroyTensor(selOut); aclrtFree(logitsDev); aclrtFree(topKDev); aclrtFree(topPDev); aclrtFree(qDev); aclrtFree(idxDev); aclrtFree(selDev); return cr; } int main() { if (aclInit(nullptr)) return 1; if (aclrtSetDevice(0)) return 1; aclrtStream stream; if (aclrtCreateStream(&stream)) return 1; // official example shape first auto ex = Run("EXAMPLE-shape k=128", 128, 48, 131072, stream); auto small = Run("SMALL k=128", 128, 1, 16384, stream); auto k0 = Run("TRIGGER k=0 V=16384", 0, 1, 16384, stream); auto k0big = Run("TRIGGER k=0 example-shape", 0, 48, 131072, stream); aclrtDestroyStream(stream); aclrtResetDevice(0); aclFinalize(); if (ex.ws != 0 && small.ws != 0) { LOG_PRINT("All controls failed — cannot reach kernel (ws ex=%d small=%d)\n", ex.ws, small.ws); return 1; } auto check = [](const CaseR& c) -> int { if (c.ws != 0) return 0; // rejected if (c.sync != 0 || c.launch != 0) return 42; if (c.idx != 1234) return 42; // for B=1 case only meaningful return 0; }; if (k0.ws == 0 && (k0.sync != 0 || k0.launch != 0)) { LOG_PRINT("VERDICT: BUG_REPRODUCED (exit 42) k=0 device fault\n"); return 42; } if (k0big.ws == 0 && (k0big.sync != 0 || k0big.launch != 0)) { LOG_PRINT("VERDICT: BUG_REPRODUCED (exit 42) k=0 big device fault\n"); return 42; } if (k0.ws == 0 && k0.idx != 1234) { LOG_PRINT("VERDICT: BUG_REPRODUCED (exit 42) k=0 wrong idx=%ld\n", (long)k0.idx); return 42; } if (k0.ws != 0 && k0big.ws != 0) { LOG_PRINT("VERDICT: NO_REPRO — k=0 host-rejected (and/or schema)\n"); return 0; } LOG_PRINT("VERDICT: NO_REPRO — k=0 behaved OK\n"); return 0; }执行脚本
文件名:
build_and_run_repro.sh#!/usr/bin/env bash # 模板:一键编译并运行同目录下的 test_aclnn_*.cpp # 用法(已 source CANN set_env.sh): # bash build_and_run_repro.sh [--vendor cann_scan_repro] [--src test_aclnn_xxx.cpp] set -euo pipefail VENDOR="" SRC_NAME="" while [[ $# -gt 0 ]]; do case "$1" in --vendor) VENDOR="$2"; shift 2 ;; --src) SRC_NAME="$2"; shift 2 ;; *) echo "unknown arg: $1" >&2; exit 2 ;; esac done : "${ASCEND_HOME_PATH:?请先 source CANN set_env.sh}" ROOT="$(cd "$(dirname "$0")" && pwd)" if [[ -z "$SRC_NAME" ]]; then # 默认取目录内唯一的 test_aclnn_*.cpp mapfile -t _srcs < <(ls "$ROOT"/test_aclnn_*.cpp 2>/dev/null || true) if [[ ${#_srcs[@]} -ne 1 ]]; then echo "请用 --src 指定测试文件(当前匹配到 ${#_srcs[@]} 个 test_aclnn_*.cpp)" >&2 exit 2 fi SRC="${_srcs[0]}" else SRC="$ROOT/$SRC_NAME" fi BIN="${SRC%.cpp}" if [[ -n "$VENDOR" ]]; then for cand in \ "${ASCEND_HOME_PATH}/vendors/${VENDOR}_nn/bin/set_env.bash" \ "${ASCEND_HOME_PATH}/vendors/${VENDOR}/bin/set_env.bash" \ "${ASCEND_HOME_PATH}/opp/vendors/${VENDOR}/bin/set_env.bash" do if [[ -f "$cand" ]]; then # shellcheck disable=SC1090 source "$cand" echo "[env] sourced $cand" echo "[env] ASCEND_CUSTOM_OPP_PATH=${ASCEND_CUSTOM_OPP_PATH:-}" break fi done fi INC="${ASCEND_HOME_PATH}/aarch64-linux/include" LIB="${ASCEND_HOME_PATH}/aarch64-linux/lib64" [[ -d "$INC" ]] || INC="${ASCEND_HOME_PATH}/include" [[ -d "$LIB" ]] || LIB="${ASCEND_HOME_PATH}/lib64" echo "[build] g++ $SRC" g++ -std=c++17 -O2 "$SRC" \ -I"$INC" -I"$INC/aclnnop" \ -L"$LIB" \ -Wl,-rpath,"$LIB" \ -lascendcl -lnnopbase -lopapi \ -o "$BIN" echo "[run] $BIN" set +e "$BIN" RC=$? set -e echo "[exit] $RC (42=BUG_REPRODUCED, 0=NO_REPRO, 1=harness error)" exit "$RC"修复建议
在 host/tiling/kernel 入口拒绝非法索引或控制量,返回明确参数错误;勿静默 OOB。
人工复核说明
Agent 复现结果供参考;语义边界(是否允许 wrap、文档是否声明调用方契约)以人工结论与产品规范为准。