已关闭
[Bug-Report|缺陷反馈]: aclnnMv cubeMathType=3(USE_HF32) + BF16 输入未报 161002(A2 文档声明不支持),HF32 被静默忽略 #7053
liuhanwen8创建于 9 天前关闭于 4 天前
9 天前 添加了label:bug-report
yuning_chen
9 天前 评论:
9 天前 评论:
9 天前 将 he_kan 设为负责人
yuning_chen
8 天前 评论:
8 天前 评论:
您好,该问题已收到,当前问题 @he_kan 处理中


8 天前 移除了负责人 he_kan
8 天前 将 yolic 设为负责人
7 天前 移除了负责人 yolic
7 天前 将 zhengyuhao3 设为负责人
4 天前 关联了pull request:修复matmul算子Issue问题
4 天前 删除了关联的pull request:修复matmul算子Issue问题
4 天前 关闭了 issue
4 天前 添加了label:resolved
问题描述
aclnnMv(ops-nnmatmul/mv)在cubeMathType=3(USE_HF32)且输入为 BF16 时,两段接口均返回 0(ACLNN_SUCCESS),仅输出 WARN 日志,HF32 语义被静默忽略(保持原 dtype 计算),未按 A2 文档约束返回 161002。复现组合:
self=bf16[4x4]ND | vec=bf16[4]ND | out=bf16[4]ND | cubeMathType=3。文档依据:
docs/aclnnMv.md「Atlas A2系列产品」节明确声明「cubeMathType=3,当输入数据类型为FLOAT32时,会转换为HFLOAT32计算,当输入为其他数据类型时不支持该选项」。代码位置:
matmul/mv/mv_common/cube_util.cppCalcUseHf32PromoteType保持原 dtype;CheckCubeMathTypeForMm对 USE_HF32+BF16/FP16 仅OP_LOGW不拒。控制组:self 3D → 161002、out dtype uint8 → 161002,报错通道正常。
环境信息
重现步骤
mv_repro.py,执行source /usr/local/Ascend/ascend-toolkit/set_env.sh。python3 mv_repro.py 4(缺陷复现,期望 161002);python3 mv_repro.py 0(合法对照)。实测输出(Atlas A2 / CANN 9.2.0):
复现脚本 mv_repro.py(自包含 ctypes,仅依赖 CANN,无第三方库)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """aclnnMv 入参校验缺失最小复现(仅依赖 CANN,无第三方库)。 用法: python3 mv_repro.py 0 # 合法对照:fp32 [4x4]x[4] cmt=0,期望两段均 0 python3 mv_repro.py 1 # BUG-1:self 为空(m=0) 且 dtype 与 vec/out 不一致,期望 161002 python3 mv_repro.py 3 # BUG-3:cubeMathType=2(USE_FP16) + BF16,期望 161002 python3 mv_repro.py 4 # BUG-4:cubeMathType=3(USE_HF32) + BF16,期望 161002 实测(Atlas A2 训练系列 / 910B2,CANN 9.2.0):case 1/3/4 两段接口均返回 0; 文档 docs/aclnnMv.md(Atlas A2 系列产品节 + 错误码表)声明上述场景应报 161002。 """ import ctypes import struct import sys ACL_SUCCESS = 0 ACL_FLOAT = 0 ACL_BF16 = 27 ACL_FORMAT_ND = 2 ACL_MEM_MALLOC_HUGE_FIRST = 0 ACL_MEMCPY_HOST_TO_DEVICE = 1 ACL_MEMCPY_DEVICE_TO_HOST = 2 acl = ctypes.CDLL("libascendcl.so", mode=ctypes.RTLD_GLOBAL) opapi = ctypes.CDLL("libopapi.so", mode=ctypes.RTLD_GLOBAL) # 关键:必须显式声明 argtypes,否则 ctypes 按 32 位 int 传指针会导致段错误 acl.aclInit.argtypes = [ctypes.c_char_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] acl.aclrtDestroyStream.argtypes = [ctypes.c_void_p] acl.aclrtResetDevice.argtypes = [ctypes.c_int32] acl.aclrtFree.argtypes = [ctypes.c_void_p] opapi.aclCreateTensor.restype = ctypes.c_void_p opapi.aclCreateTensor.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] gws = opapi.aclnnMvGetWorkspaceSize run = opapi.aclnnMv gws.restype = ctypes.c_int run.restype = ctypes.c_int gws.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int8, ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.c_void_p)] run.argtypes = [ctypes.c_void_p, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p] def chk(rc, what): if int(rc) != 0: raise RuntimeError("%s rc=%d" % (what, int(rc))) class RT: def __init__(self): chk(acl.aclInit(None), "aclInit") chk(acl.aclrtSetDevice(0), "aclrtSetDevice") self.stream = ctypes.c_void_p() chk(acl.aclrtCreateStream(ctypes.byref(self.stream)), "aclrtCreateStream") def tensor(self, raw, shape, dtype): """raw: host bytes;shape: 维度列表;dtype: ACL_FLOAT / ACL_BF16。""" ptr = ctypes.c_void_p() chk(acl.aclrtMalloc(ctypes.byref(ptr), ctypes.c_size_t(max(len(raw), 8)), ACL_MEM_MALLOC_HUGE_FIRST), "aclrtMalloc") if raw: chk(acl.aclrtMemcpy(ptr, ctypes.c_size_t(len(raw)), ctypes.cast(ctypes.c_char_p(raw), ctypes.c_void_p), ctypes.c_size_t(len(raw)), ACL_MEMCPY_HOST_TO_DEVICE), "H2D") dims = (ctypes.c_int64 * len(shape))(*shape) strides = [1] * len(shape) for i in range(len(shape) - 2, -1, -1): strides[i] = shape[i + 1] * strides[i + 1] st = (ctypes.c_int64 * len(shape))(*strides) h = opapi.aclCreateTensor(dims, ctypes.c_uint64(len(shape)), ctypes.c_int32(dtype), st, ctypes.c_int64(0), ctypes.c_int32(ACL_FORMAT_ND), dims, ctypes.c_uint64(len(shape)), ptr) return ctypes.c_void_p(h), ptr def read_f32(self, ptr, n): buf = ctypes.create_string_buffer(4 * n) chk(acl.aclrtMemcpy(ctypes.cast(buf, ctypes.c_void_p), ctypes.c_size_t(4 * n), ptr, ctypes.c_size_t(4 * n), ACL_MEMCPY_DEVICE_TO_HOST), "D2H") return list(struct.unpack("<%df" % n, buf.raw)) def done(self): acl.aclrtDestroyStream(self.stream) acl.aclrtResetDevice(0) BF16_ONE = struct.pack("<H", 0x3F80) # bf16(1.0) F32_ONE = struct.pack("<f", 1.0) def call(rt, self_raw, self_shape, self_dt, vec_raw, vec_shape, vec_dt, out_raw, out_shape, out_dt, cmt): s_h, s_p = rt.tensor(self_raw, self_shape, self_dt) v_h, v_p = rt.tensor(vec_raw, vec_shape, vec_dt) o_h, o_p = rt.tensor(out_raw, out_shape, out_dt) ws = ctypes.c_uint64(0) ex = ctypes.c_void_p() rc1 = gws(s_h, v_h, o_h, ctypes.c_int8(cmt), ctypes.byref(ws), ctypes.byref(ex)) rc2 = None if int(rc1) == 0 and ex.value: wsbuf = ctypes.c_void_p() if ws.value > 0: chk(acl.aclrtMalloc(ctypes.byref(wsbuf), ctypes.c_size_t(ws.value), ACL_MEM_MALLOC_HUGE_FIRST), "ws malloc") rc2 = run(wsbuf, ctypes.c_uint64(ws.value), ex, rt.stream) acl.aclrtSynchronizeStream(rt.stream) if wsbuf.value: acl.aclrtFree(wsbuf) out = None if int(rc1) == 0 and int(rc2 or 0) == 0 and out_dt == ACL_FLOAT and out_shape: out = rt.read_f32(o_p, out_shape[0]) return rc1, rc2, ws.value, out def main(): sel = int(sys.argv[1]) if len(sys.argv) > 1 else 0 rt = RT() try: if sel == 0: self_raw = struct.pack("<16f", *[float(i + 1) for i in range(16)]) rc1, rc2, ws, out = call(rt, self_raw, [4, 4], ACL_FLOAT, F32_ONE * 4, [4], ACL_FLOAT, b"\xAA" * 16, [4], ACL_FLOAT, 0) print("case 0 (legal control): rc1=%d rc2=%d ws=%d out=%s " "(expect rc1=0 rc2=0 out=[10.0, 26.0, 42.0, 58.0])" % (rc1, rc2, ws, out)) elif sel == 1: # BUG-1:self 空(m=0) 且 dtype 与 vec/out 不一致 rc1, rc2, ws, _ = call(rt, b"", [4, 0], ACL_FLOAT, b"", [0], ACL_BF16, b"\xAA" * 8, [4], ACL_BF16, 0) print("case 1 (empty self m=0, dtype mismatch fp32 vs bf16): " "rc1=%d rc2=%d ws=%d (expect 161002)" % (rc1, rc2, ws)) elif sel == 3: # BUG-3:cmt=2(USE_FP16) + BF16 rc1, rc2, ws, _ = call(rt, BF16_ONE * 16, [4, 4], ACL_BF16, BF16_ONE * 4, [4], ACL_BF16, b"\xAA" * 8, [4], ACL_BF16, 2) print("case 3 (cubeMathType=2 USE_FP16 + BF16): " "rc1=%d rc2=%d ws=%d (expect 161002)" % (rc1, rc2, ws)) elif sel == 4: # BUG-4:cmt=3(USE_HF32) + BF16 rc1, rc2, ws, _ = call(rt, BF16_ONE * 16, [4, 4], ACL_BF16, BF16_ONE * 4, [4], ACL_BF16, b"\xAA" * 8, [4], ACL_BF16, 3) print("case 4 (cubeMathType=3 USE_HF32 + BF16): " "rc1=%d rc2=%d ws=%d (expect 161002)" % (rc1, rc2, ws)) else: print("unknown case %d" % sel) return 2 return 0 finally: rt.done() if __name__ == "__main__": sys.exit(main())预期结果
第一段返回 161002(ACLNN_ERR_PARAM_INVALID,对应文档错误码表「传入的cubeMathType不符合约束」)。
备注