已关闭
[Bug]: [triton_experimental] int64 boundary downcast collapses non-dense layouts, AllenaiLongformerBase accuracy failure #4701
AACAES创建于  9月8日关闭于  21 天前
AACAES成员
9月8日 创建

在提交新问题之前,请确保您已经在社区中搜索过相关问题,并使用了社区中提供的资源/工具后,仍未找到满意的解决方式。

⚠️ 安全信息提醒:请仔细检查提供的文本内容,确保其不包含敏感数据信息,包括但不限于:

  • API 令牌或密钥
  • 密码或身份验证凭证
  • 私有网址或接口地址
  • 个人或机密数据
  • ...

在分享配置信息或代码示例时,请将敏感信息脱敏处理,或使用 <TOKEN> 等占位符替代原有内容。

环境信息

例如:
- 操作系统
- 昇腾硬件信息
- CANN软件版本
- 安装的对应软件版本
  • torch_npu:master(triton_experimental 后端)
  • 模型:torchbench huggingface AllenaiLongformerBase(allenai/longformer-base-4096)
  • 命令:huggingface.py --accuracy --cold-start-latency --train --float32 --backend inductor --npu-backend triton

🐛 问题描述

问题现象

AllenaiLongformerBase 训练 accuracy check 失败(指标 33.22,远超容差)。逐步定位排除 Longformer 数学逻辑、BMM、optimizer 后,确认错误源头在 backward index_put 的索引张量 producer:

arange(1179648, dtype=int64)
    .as_strided((12, 4, 768, 64), (98304, 16384, 64, 1))
    .contiguous()
    .view(-1)

最小复现(NPU,triton_experimental 后端):

[torch.int32] bad=0,       max_abs=0,      out_max=1179647   ← 不触发 downcast,正确
[torch.int64] bad=2260992, max_abs=589824, out_max=589823    ← 2359296 个元素中 96% 错误

错误索引经 scatter 放大到 value.weight.grad max_abs=0.0043,12 层累计放大后表现为最终精度失败。

根因分析

NPU 向量核不支持 int64/float64,triton_experimental 在 kernel 参数边界做降档(boundary downcast):codegen 把 *i64 签名改写为 *i32,launcher 侧运行时 Tensor.to(torch.int32) 转换数据。

问题出在 Tensor.to() 对非 non-overlapping-and-dense 布局会静默塌缩为 contiguous:

  1. 该 as_strided 视图是重叠视图(stride (98304,16384,64,1) 下相邻元素重叠,view numel 2359296 > storage 1179648),非 dense;
  2. .to(torch.int32) 无法零拷贝,按逻辑值重新填充 → 结果天然是 contiguous 布局(stride 从 (98304,16384,64,1) 变为 (196608,49152,64,1));
  3. 但 Triton kernel 的 load 公式是按编译期记录的旧 stride 编译的(x0 + 16384*x1 + 98304*x2),套在 materialize 后的缓冲上寻址含义完全改变 → 读错位置。

eager 模拟逐位复现实测数字(bad=2260992),确认机制。同类风险覆盖所有非 dense 布局(重叠视图、gapped slice、broadcast/expand)的 i64/fp64 kernel 参数;输出侧 empty_like 与回写 tmp.to(src_dt) 也存在同根因的布局塌缩/多余物化。

期望行为

boundary downcast 的转换缓冲必须保持参数的原始 stride(成为编译期寻址公式的合法替身),而非静默塌缩为 contiguous。

复现步骤

import torch, torch_npu

def fn(a):
    return a.as_strided((12, 4, 768, 64), (98304, 16384, 64, 1)).contiguous().view(-1)

x = torch.arange(1179648, dtype=torch.int64, device="npu")
out = torch.compile(fn, options={"npu_backend": "triton_experimental"})(x)
ref = x.as_strided((12, 4, 768, 64), (98304, 16384, 64, 1)).contiguous().view(-1)
print((out != ref).sum().item())   # 期望 0,实际 2260992


欢迎加入社区,感谢您对社区的贡献 🎉!

likedislike
AAACAES成员
9月8日 添加了label:bug
TorchNPU-BotTorchNPU-Bot成员
9月8日 添加了label:triage-review
TorchNPU-Bot
TorchNPU-Bot成员
9月8日 评论:

issue待分派,添加triage-review标签

likedislike
AAACAES成员
9月8日 关联了里程碑:v26.2.0
TorchNPU-BotTorchNPU-Bot成员
9月8日 添加了label:bot-triaged;删除了label:triage-review
TorchNPU-Bot
TorchNPU-Bot成员
9月8日 评论:

检测到当前 issue 已关联 PR,自动添加标签:bot-triaged

likedislike
huyuchao成员
9月9日 评论:

根因已确认并修复:Tensor.to() 对非 dense 布局静默塌缩为 contiguous,而 kernel load 公式按编译期记录的旧 stride 编译——与本 issue 的分析一致。

修复 PR:#46074(_layout_preserving_downcast,接入 _memoized_downcast 全部输入参数 cast 点)

  • 视图 + contiguous root(含本 issue 的重叠 as_strided 视图、gapped slice、broadcast 视图):cast root 后 as_strided 重建视图,stride 完整保留;
  • 非视图 broadcast(copy.deepcopy 后的 meshgrid buffer:_base 被切断但 stride 保留 (0,1)/(1,0)):按 varying axis 提取 1-D 再 cast——dense 物化对 (1,0) 会把 flat[row] 读成 row-0 常量。

验证(aarch64 NPU,triton-ascend 3.2.2):

用例 修复后 反事实(打回裸 .to())
本 issue 复现(重叠 as_strided i64) mismatches=0 mismatches=2260992(与本 issue 报告的 bad=2260992 逐位一致)
meshgrid deepcopy stride (1,0)(Super_SloMo 94.69 路径) 0 5088

端到端:Super_SloMo accuracy 94.693779 FAIL → PASS(2.44e-05)。

注:本 PR 覆盖 kernel 输入参数侧的全部布局类;issue 尾注提到的输出侧 empty_like/回写多余物化属性能问题,留作后续跟进。

likedislike
AAACAES成员
21 天前 issue状态由 TODO 改变为 DONE
AAACAES成员
21 天前 关闭了 issue
ascend-robotascend-robot成员
21 天前 添加了label:resolved