已关闭
[Bug-Report|缺陷反馈]: aclnnFusedQuantMatmulWeightNz 预留入参传非空返回 161001,资料声明 161002 #7064
liuhanwen8创建于  12 天前关闭于  7 天前
liuhanwen8
liuhanwen8
12 天前 创建

环境

  • Atlas A2 训练系列(910B2,npu-smi 26.1.1)
  • CANN 9.2.0(Ascend-cann-toolkit_9.2.0_linux-aarch64)
  • 接口:aclnnFusedQuantMatmulWeightNz(两段式,WeightNz 权重通路)
  • 资料:matmul/fused_quant_mat_mul/docs/aclnnFusedQuantMatmulWeightNz.md

现象

资料《返回值》表把「传入的当前不支持的 yScaleOptional、x1OffsetOptional、x2OffsetOptional、yOffsetOptional、x3Optional、groupSizeOptional 参数」明确归入 161002(ACLNN_ERR_PARAM_INVALID) 的第 5 条子项;
实测这些预留入参一旦传值(给一个合法张量 / 非 0 的 groupSize),第一段接口返回的是 161001(ACLNN_ERR_PARAM_NULLPTR)。

预留入参 传值形态 实测返回 资料声明
yScaleOptional fp32[32] 张量 161001 161002
x1OffsetOptional fp32[32] 张量 161001 161002
x2OffsetOptional fp32[32] 张量 161001 161002
yOffsetOptional fp32[32] 张量 161001 161002
x3Optional fp32[32] 张量 161001 161002
groupSizeOptional 16 161001 161002

不开/开确定性(ACL_OPT_DETERMINISTIC = 0/1)两种场景结论一致。

影响

调用方按资料实现「非法参数 → 按 161002 分支处理」时拿不到预期错误码:拿到 161001 会被误判为「某个必选入参传了空指针」,把排查方向带偏(尤其 groupSizeOptional 是标量,跟空指针毫无关系)。

复现(自包含,只依赖 CANN 自带 libascendcl / libopapi / libnnopbase + ctypes)

source /usr/local/Ascend/ascend-toolkit/set_env.sh
python3 fqmmnz_reserved_optional.py

实测输出:

x2 声明维度 = [32, 32]
yScaleOptional = nullptr   -> (0, 0)
yScaleOptional = fp32[32]  -> (161001, None)   # 资料声明应为 161002
import ctypes, sys

M = K = N = 32
# 可选:用第一个命令行参数指定 x2 的**声明维度**(逗号分隔),用于核对资料的 4~8 维声明
X2_SHAPE = [int(v) for v in sys.argv[1].split(",")] if len(sys.argv) > 1 else [K, N]
INT8, F32, F16 = 2, 0, 1
ND, NZ = 2, 29
acl = ctypes.CDLL("libascendcl.so")
op = ctypes.CDLL("libopapi.so")
try:
    nb = ctypes.CDLL("libnnopbase.so")
except OSError:
    nb = None


def sym(n):
    for lib in (acl, nb):
        if lib is not None:
            try:
                return getattr(lib, n)
            except AttributeError:
                pass
    raise AttributeError(n)


def chk(r, w):
    if int(r) != 0:
        raise RuntimeError("%s -> %d" % (w, int(r)))


acl.aclInit.argtypes = [ctypes.c_void_p]
acl.aclrtSetDevice.argtypes = [ctypes.c_int32]
acl.aclrtCreateStream.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
acl.aclrtMalloc.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.c_size_t, ctypes.c_int32]
acl.aclrtMemcpy.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int32]
acl.aclrtSynchronizeStream.argtypes = [ctypes.c_void_p]
mk_t = sym("aclCreateTensor")
mk_t.restype = ctypes.c_void_p
mk_t.argtypes = [ctypes.POINTER(ctypes.c_int64), ctypes.c_uint64, ctypes.c_int32,
                 ctypes.POINTER(ctypes.c_int64), ctypes.c_int64, ctypes.c_int32,
                 ctypes.POINTER(ctypes.c_int64), ctypes.c_uint64, ctypes.c_void_p]
mk_a = sym("aclCreateIntArray")
mk_a.restype = ctypes.c_void_p
mk_a.argtypes = [ctypes.POINTER(ctypes.c_int64), ctypes.c_uint64]
op.aclnnCalculateMatmulWeightSizeV2.restype = ctypes.c_int32
op.aclnnCalculateMatmulWeightSizeV2.argtypes = [ctypes.c_void_p, ctypes.c_int32, ctypes.POINTER(ctypes.c_uint64)]
op.aclnnTransMatmulWeightGetWorkspaceSize.restype = ctypes.c_int32
op.aclnnTransMatmulWeightGetWorkspaceSize.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.c_void_p)]
op.aclnnTransMatmulWeight.restype = ctypes.c_int32
op.aclnnTransMatmulWeight.argtypes = [ctypes.c_void_p, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p]
op.aclnnFusedQuantMatmulWeightNzGetWorkspaceSize.restype = ctypes.c_int32
op.aclnnFusedQuantMatmulWeightNz.restype = ctypes.c_int32

chk(acl.aclInit(None), "aclInit")
chk(acl.aclrtSetDevice(0), "aclrtSetDevice")
stream = ctypes.c_void_p()
chk(acl.aclrtCreateStream(ctypes.byref(stream)), "aclrtCreateStream")


def dev(data=b"", need=0):
    p = ctypes.c_void_p()
    chk(acl.aclrtMalloc(ctypes.byref(p), max(len(data), need, 1), 0), "aclrtMalloc")
    if data:
        b = ctypes.create_string_buffer(data, len(data))
        chk(acl.aclrtMemcpy(p, max(len(data), need, 1), b, len(data), 1), "aclrtMemcpy")
    return p


def tensor(shape, dt, ptr, fmt=ND, storage=None):
    dims = (ctypes.c_int64 * len(shape))(*shape)
    st = [1] * len(shape)
    for i in range(len(shape) - 2, -1, -1):
        st[i] = shape[i + 1] * st[i + 1]
    strides = (ctypes.c_int64 * len(shape))(*st)
    storage = list(storage or shape)
    sd = (ctypes.c_int64 * len(storage))(*storage)
    return mk_t(dims, len(shape), dt, strides, 0, fmt, sd, len(storage), ctypes.c_void_p(ptr.value))


x1 = dev(bytes([1] * (M * K)))
s1 = dev(bytes((ctypes.c_float * M)(*([0.5] * M))))
s2 = dev(bytes((ctypes.c_float * N)(*([0.5] * N))))
out = dev(b"", M * N * 2)

# ---- x2:先按 ND 声明,再用 aclnnTransMatmulWeight 原地刷成亲和排布,最后按 NZ 声明 ----
wraw = bytes([1] * (K * N))
arr = (ctypes.c_int64 * len(X2_SHAPE))(*X2_SHAPE)
ia = mk_a(arr, len(X2_SHAPE))
size = ctypes.c_uint64(0)
chk(op.aclnnCalculateMatmulWeightSizeV2(ctypes.c_void_p(ia), INT8, ctypes.byref(size)), "WeightSize")
wptr = dev(wraw, int(size.value))
tmp = tensor(X2_SHAPE, INT8, wptr, ND, [int(size.value)])
ws = ctypes.c_uint64(0)
ex = ctypes.c_void_p()
chk(op.aclnnTransMatmulWeightGetWorkspaceSize(ctypes.c_void_p(tmp), ctypes.byref(ws), ctypes.byref(ex)), "Trans1")
wsp = ctypes.c_void_p()
if ws.value:
    chk(acl.aclrtMalloc(ctypes.byref(wsp), int(ws.value), 0), "malloc")
chk(op.aclnnTransMatmulWeight(wsp, ws.value, ex, stream), "Trans2")
chk(acl.aclrtSynchronizeStream(stream), "sync")
x2 = tensor(X2_SHAPE, INT8, wptr, NZ, [int(size.value)])

t1 = tensor([M, K], INT8, x1)
ts1 = tensor([M], F32, s1)
ts2 = tensor([N], F32, s2)
tout = tensor([M, N], F16, out)
yScale = tensor([N], F32, s2)      # 资料要求这个位置必须是 nullptr


def call(yscale):
    ws = ctypes.c_uint64(0)
    ex = ctypes.c_void_p()
    null = ctypes.c_void_p(None)
    h = lambda t: ctypes.c_void_p(t) if t else null
    r1 = op.aclnnFusedQuantMatmulWeightNzGetWorkspaceSize(
        h(t1), h(x2), h(ts1), h(ts2), h(yscale), null, null, null, null, null,
        ctypes.c_char_p(b"gelu_tanh"), ctypes.c_int64(0), h(tout),
        ctypes.byref(ws), ctypes.byref(ex))
    r2 = None
    if r1 == 0:
        p = ctypes.c_void_p()
        if ws.value:
            chk(acl.aclrtMalloc(ctypes.byref(p), int(ws.value), 0), "malloc2")
        r2 = op.aclnnFusedQuantMatmulWeightNz(p, ws.value, ex, stream)
        chk(acl.aclrtSynchronizeStream(stream), "sync2")
    return r1, r2


print("x2 声明维度 =", X2_SHAPE)
print("yScaleOptional = nullptr   ->", call(None))
print("yScaleOptional = fp32[32]  ->", call(yScale), "  # 资料声明应为 161002")

期望

预留入参传非空 / 非 0 时返回 161002(与资料一致);或资料改为 161001 并说明「预留参数必须为 nullptr,否则按空指针错误码返回」。

likedislike
yuning_chen
yuning_chen成员
12 天前 评论:

初步分析

感谢反馈。

现象理解:aclnnFusedQuantMatmulWeightNz 的预留入参传非空时返回 161001,资料声明是 161002。

当前判断:初步判断,待复现确认。

涉及范围:fused_quant_matmul 的预留参数错误码。

下一步:给预留入参传非空值看返回码。确认后改成 161002。

likedislike
yuning_chenyuning_chen成员
11 天前 将 lifei265 设为负责人
yuning_chen
yuning_chen成员
11 天前 评论:

您好,该问题已收到,当前问题 @yolic 处理中

likedislike
yuning_chenyuning_chen成员
11 天前 将 yolic 设为负责人,移除负责人 lifei265
yuning_chenyuning_chen成员
10 天前 移除了负责人 yolic
yuning_chenyuning_chen成员
10 天前 将 zhengyuhao3 设为负责人
Zzhengyuhao3成员
7 天前 关联了pull request:修复matmul算子Issue问题
Zzhengyuhao3成员
7 天前 删除了关联的pull request:修复matmul算子Issue问题
CANN-robotCANN-robot成员
7 天前 关闭了 issue
CANN-robotCANN-robot成员
6 天前 添加了label:resolved