Closed
[Bug-Report|缺陷反馈]: aclnnBlockAttentionResidualsGrad 冷启动态首次调用必失败(361001 / coreNum is 0,TilingParse 硬依赖 GetCoreNumAiv) #6618
liuhanwen8Add on  17 days agoClosed at  9 days ago
liuhanwen8
liuhanwen8
17 days ago Create

现象

aclnnBlockAttentionResidualsGrad 在 torch_npu 路径下,若这次调用是进程内第一次在 NPU 上执行算子(platform info 尚未被任何算子执行链初始化),则以 FLOAT32 输入调用必失败,报:

AclNN_Runtime_Error(EZ9903): InitTilingParseCtx failed
  coreNum is 0[FUNC:TilingPrepareForBlockAttentionResidualsGrad]
         [FILE:block_attention_residuals_grad_tiling.cpp][LINE:522]
  Op [BlockAttentionResidualsGrad] tiling compile info parse failed
  Launch failed for BlockAttentionResidualsGrad, errno:361001

同进程第二次调用即恢复正常。

最小复现

import torch, torch_npu
from cann_ops_transformer import block_attention_residuals_backward

torch.npu.set_device(0)
T, N, H = 64, 4, 2048
torch.manual_seed(0)
# 关键:全部用 .to("npu") 纯拷贝构造,不在 NPU 上触发任何算子
pb  = torch.randn(T, H).to("npu").float()
br  = torch.randn(T, N, H).to("npu").float()
pw  = torch.randn(1, H).to("npu").float()
nw  = torch.randn(H).to("npu").float()
go  = torch.randn(T, H).to("npu").float()
inv = (torch.rand(T, N + 1) + 0.5).to("npu")
pr  = torch.softmax(torch.randn(T, N + 1), dim=1).to("npu")

out = block_attention_residuals_backward(pb, br, pw, nw, go, inv, pr)  # 首次调用 -> 失败
torch.npu.synchronize()

证据(每个数字均为实测)

对照 结果
默认异步模式,新进程首发 FAIL 6/6
设 ASCEND_LAUNCH_BLOCKING=1 OK 3/3
同进程第二次及以后 OK 16/16
先跑任意算子(add / mul)再调 OK 6/6
仅做 .to("npu") / clone 纯拷贝,不跑算子 FAIL 3/3
npu:0 / npu:1 / npu:2 / npu:3 全部 FAIL
不同 shape(H=2048/8192、N=4/128、T=64/256) 全部首发 FAIL

归属对照(关键):

路径 冷启动态首发
C++ 直接调 aclnnBlockAttentionResidualsGradGetWorkspaceSize OK 3/3(rc=0, ws=17170944)
先调 aclnn 直调成功,再走 torch 路径 torch 首次仍 FAIL
正向 BlockAttentionResiduals(torch 路径,同冷启动态) OK 3/3
反向 BlockAttentionResidualsGrad(torch 路径,同冷启动态) FAIL 3/3

即:aclnn 本体、平台初始化均正常;同一仓、同一条 ACLNN_CMD 宏链路下,正向能过、反向不过。

根因

op_host/block_attention_residuals_grad_tiling.cpp:522 在 TilingParse 阶段读取并硬判核数:

static ge::graphStatus TilingPrepareForBlockAttentionResidualsGrad(gert::TilingParseContext* context)
{
    ...
    platform_ascendc::PlatformAscendC plat(context->GetPlatformInfo());
    uint32_t aivNum = plat.GetCoreNumAiv();
    compileInfo->coreNum = aivNum;
    OP_CHECK_IF(compileInfo->coreNum == 0, OP_LOGE(context, "coreNum is 0"), return ge::GRAPH_FAILED);
    ...
}

对比同仓两个同族算子,都把核数获取放在 Tiling 主流程、TilingParse 只做指针检查:

  • 正向 mhc/block_attention_residuals:TilingParse 只校 platformInfo != nullptr,不读核数;
  • attention/chunk_gated_delta_rule:TilingParse 只校指针;GetCoreNumAiv() 在 InitCompileInfo()(Tiling 主流程)里,且取不到时只打印日志不返回失败。

TilingParse 是 kernel 生成前的一次性解析,此时平台资源表可能尚未就绪,GetCoreNumAiv() 返回 0;反向把它当作致命错误直接 GRAPH_FAILED,而正向不依赖它故不受影响。

影响

任何默认异步模式下、以 FLOAT32 输入的首次调用(真实训练/推理首个 step、单测首例、脚本首次跑)都会失败。报错落在 tiling 内部且只写 coreNum is 0,调用方难以自查。属开箱即用阻塞级问题。

补充:FP16/BF16 输入通常不复现,因为 tensor.to(torch.float16) 会插入 cast kernel 顺带完成预热;FLOAT32 的 .to(torch.float32) 是 no-op,因此暴露本问题。

建议修复

  1. (推荐) 参照正向与 chunk_gated_delta_rule:TilingParse 只做指针检查,核数下沉到 Tiling 主流程获取;取不到时降级而非硬失败。
  2. 若因 tiling key 需要在 parse 阶段定型,至少不要把 coreNum == 0 判为致命错误,改为在主流程回填。
  3. 报错信息补充上下文(如「平台信息未初始化,GetCoreNumAiv 返回 0」),便于定位。

环境信息

  • 仓库版本:cann/ops-transformer master
  • CANN:9.2.0(npu-smi 26.1.1)
  • Python 3.12.15 / torch 2.12.0+cpu / torch_npu 2.12.0.post3
  • 设备:Atlas A2 训练系列产品(Ascend910B2),8 卡均复现

备注

validBlockNum 文档与实现的不一致(文档称仅支持 -1、实现允许 -1 或 N)已由 #6125 覆盖,本 issue 不重复报告。

likedislike
周云飞
周云飞Collaborators
17 days ago Comments:

感谢提供完整的复现脚本和冷启动对照,尤其是正反向、直调与 torch 路径的差异,这些信息有助于缩小定位范围。

问题分析

从您报告的结果看,问题集中在 Atlas A2、CANN 9.2.0 环境下,torch_npu 默认异步路径中未经过算子预热的 FLOAT32 首次调用。日志显示失败发生在 TilingParse 阶段,而不是反向计算 kernel 执行阶段。预热后成功、纯拷贝后仍失败的对照,支持优先排查平台信息的获取时机与调用路径差异。

您贴出的 TilingPrepareForBlockAttentionResidualsGrad 片段中,GetCoreNumAiv() 返回 0 后会直接返回 GRAPH_FAILED,这能够解释日志中的解析失败。不过,核数为什么在该路径下为 0,以及是否确由平台资源表尚未就绪引起,仍需进一步确认。C++ 直调 GetWorkspaceSize 成功说明该对照路径未触发同样的失败,但还不足以排除 torch 路径中平台信息传递或初始化时序的问题,也不等同于完整执行链已经验证通过。

处理方向

一个可行的方向是调整反向算子对平台信息的使用时机:在 TilingParse 中保留必要的指针检查,将核数读取及有效性检查移到实际使用核数的 Tiling 主流程。调整时需要确认 compileInfo->coreNum 的后续用途,确保分核计算和 kernel 启动前取得有效核数,不能仅删除报错后继续使用 0。若考虑降级,也需要先明确安全的核数来源和适用条件。

报错信息可以补充失败阶段、核数读取结果和必要的平台上下文;在初始化原因尚未确认前,不宜直接将错误归因为“平台信息未初始化”。

短期内,可按您已验证的方式在调用前执行一次 add/mul 作为临时绕过;ASCEND_LAUNCH_BLOCKING=1 更适合诊断,因为它会改变异步执行行为,不建议作为长期处理方式。我们会继续确认核数获取时机及其下游依赖。本条聚焦冷启动失败,不重复展开 validBlockNum 的文档问题。

likedislike
周云飞周云飞Collaborators
14 days ago Assign zzzzzzyf_ as responsible person
周云飞周云飞Collaborators
11 days ago Added label: requirement
周云飞周云飞Collaborators
9 days ago Associated pull request
CANN-robotCANN-robotCollaborators
9 days ago Close issue
CANN-robotCANN-robotCollaborators
9 days ago Added label: resolved