[Bug已解决] Inductor 生成内核调用 empty_strided_cpu 负步长报错 Expected result 0 解决方案一、现象长什么样你用torch.compile编译了一个模型运行时却崩在 InductorPyTorch 的后端代码生成器生成的一段内核里报错形如[inductor] generated kernel calls empty_strided_cpu with a negative stride → Expected result 0或者更完整一点RuntimeError: Expected result 0, but got -N ... in empty_strided_cpu ... in inductor generated kernel triton_xxx意思是Inductor 在为一堆张量分配内存时计算出了一个「负的 stride步长」传给empty_strided_cputorch.empty_strided的 CPU 版而empty_strided要求 stride 必须 ≥ 0于是断言Expected result 0失败内核直接崩。 本 issuepytorch/pytorch#188227说的是Inductor 生成的某个 kernel 在推导输出张量的 stride 时算出了负数 stride导致 CPU 上的empty_strided_cpu调用失败。注意它常发生在 CPU 路径或某些 CPU fallback但根因在 Inductor 的 stride 推导逻辑。 本文聚焦empty_strided的 stride 约束、negative stride是怎么被 Inductor 算出来的、怎么绕过与修复。二、背景empty_strided 与 stride 约束torch.empty_strided(size, stride)创建一个按指定stride布局的张量import torch # 正常size(2,3), stride(3,1) 是连续的 t torch.empty_strided((2, 3), (3, 1)) print(t.stride()) # (3, 1)关键点stride的每个元素必须是非负整数 0。负 stride 在物理上意味着「反向遍历」但empty_strided的实现要求非负它假定内存按正步长分配。所以一旦传入负数就触发Expected result 0。 合法的反向视图怎么办用torch.as_strided或flip/ 转置得到而不是empty_strided直接给负 stride# 想要「看起来反向」的视图用 as_strided同样要求非负 stride 才可分配 # 或直接用 .flip()/.t() 得到逻辑上的反向底层 stride 仍非负 a torch.arange(6).reshape(2, 3) b a.t() # 转置stride 变了但非负 print(b.stride()) # (1, 2) —— 仍是合法的 c a.flip(1) # 逻辑反向底层是新的连续张量 print(c.stride()) # (3, 1)所以empty_strided永远不该拿到负 strideInductor 生成代码里出现负 stride是它的 stride 推导 bug。三、为什么 Inductor 会生成负 strideInductor 把 PyTorch 的 FX 图翻译成 Triton / CPU 内核时需要为每个中间张量推导内存布局stride。推导中有一类优化叫「stride 传播 / layout 推断」根据输入 stride 和算子的形状变换推断输出 stride以保留连续/非连续信息、减少copy_。 负 stride 出现的典型场景某个算子的 stride 变换公式在动态形状 / 特殊维度下算错比如把一个stride乘以-1来表示「该维被翻转」但这个中间 stride 又被直接喂给empty_strided_cpu而empty_strided不接受负号。view / reshape 的推导把负维度卷入 stride 计算当存在view把某维 size 变成 1、又叠加转置语义时推导出的组合 stride 可能为负。CPU fallback 路径某些算子在 GPU 没有实现、回退到 CPU 内核CPU 内核用的是empty_strided_cpu于是负 stride 在 CPU 上暴露GPU 端empty_strided可能容忍或行为不同掩盖了 bug。 简言之Inductor 的 layout 推断给empty_strided喂了个它不该接受的负 stride。这是 Inductor 代码生成阶段的逻辑缺陷不是你模型写错。四、最小可运行复现带守卫下面演示「empty_strided负 stride 直接崩」的原语以及「在torch.compile下可能间接触发」的结构。负 stride 原语无需 GPUcompile 触发需环境用守卫包裹。import torch def demo_empty_strided_negative(): # 直接演示负 stride 必然触发 Expected result 0 try: t torch.empty_strided((2, 3), (3, -1)) # 负 stride except RuntimeError as e: print([复现] empty_strided 负 stride 报错:, e) def demo_via_compile(): # torch.compile 下Inductor 可能在某些 shape 变换里生成负 stride if not torch.cuda.is_available(): # CPU 上也能触发 inductor 的 CPU 内核路径 pass def f(x): # 一些会触发 stride 推断的 op 组合示意 y x.transpose(0, 1) z y.contiguous() # 某些布局下 Inductor 推导 stride return z * 2 x torch.randn(4, 8) try: g torch.compile(f, backendinductor) out g(x) print(compile 输出形状:, out.shape) except RuntimeError as e: if empty_strided in str(e) and result 0 in str(e): print([确认] Inductor 生成负 stride 命中本 bug:, e) else: print(其它错误:, e) if __name__ __main__: demo_empty_strided_negative() demo_via_compile()要点demo_empty_strided_negative直接证明「负 stride 必崩」demo_via_compile是 Inductor 触发路径的示意实际触发依赖具体模型结构和版本。五、解决方案一关闭 Inductor 的 layout 优化快速绕过最直接绕过让 Inductor 不做激进的 stride 推断或干脆换一个 backendimport torch # 方案 A强制输出连续避免负 stride 布局传播 torch.compile(options{force_stride_order: False}) def my_model(x): ... # 方案 B关闭 inductor 的「保留布局」优化强制所有中间连续 torch._inductor.config.triton_conv_heuristics False # 视版本 torch._inductor.config.coordinate_descent_tuning False # 方案 C换用非 inductor backend如 eagers 的 aot_eager仅调试 # g torch.compile(f, backendeager)更稳的是直接禁用 inductor 的「freezing」与「layout 优化」相关开关具体名字随版本变化常见import torch._inductor.config as cfg cfg.triton.use_block_ptr False # 某些版本下关闭可绕过如仍崩降级 backend 为 eager / aot_eager验证模型本身逻辑正确只是没编译加速确认只是 inductor stride 推导的问题。六、解决方案二强制 contiguous 切断负 stride 传播如果你知道哪一步触发可在该步后加.contiguous()让 Inductor 不再基于「带负号语义的 stride」去推断下游import torch def safe_block(x): y x.transpose(0, 1) y y.contiguous() # 切断负/特殊 stride回归标准正 stride z y.view(y.size(0), -1) return z * 2原理contiguous()会生成一个新的、stride 全非负的连续张量下游 Inductor 拿到的是「干净」布局不再带上可能为负的中间 stride从而避免喂给empty_strided_cpu负数。七、解决方案三升级 PyTorch 到修复版本本 issue#188227是 Inductor 的已知缺陷官方会随版本修。升级后验证pip install --upgrade torch --index-url https://download.pytorch.org/whl/cu124 python -c import torch; print(torch.__version__)判断修复的标准同样模型torch.compile(backendinductor)不再报empty_strided_cpu ... Expected result 0且可正常产出内核。 临时在 CI 里规避对触发该 bug 的模型先不加torch.compile或加torch._dynamo.config.suppress_errors True让编译失败自动回退 eager仅应急会失去加速import torch._dynamo torch._dynamo.config.suppress_errors True # 编译失败自动退回 eager不崩八、解决方案四缩小触发面动态形状 / 特殊维度负 stride 常在动态形状或含 1 维 / 转置 / view 组合时触发。可临时把dynamicTrue关掉torch.compile(f, dynamicFalse)用静态形状避开推导分支避免把transpose后的张量直接塞进容易触发 stride 推断的算子先.contiguous()用torch.export代替torch.compile看是否绕过export 走另一套布局推导。九、排查清单报错含empty_strided_cpuExpected result 0→ 确认是 Inductor 负 stride bug#188227。先torch._dynamo.config.suppress_errors True应急回退 eager确认模型逻辑本身没错。在触发算子的关键步骤后加.contiguous()切断负 stride 传播。关torch.compile(dynamicTrue)改用静态形状或换backendeager验证。升级 PyTorch 到修复版本CI 里对触发模型暂时禁用 inductor。若想贡献修复定位 Inductorir.py/layout推断里把 stride 变负的位置确保传给empty_strided前stride.clamp_min(0)或改用as_strided的合法路径。十、小结[inductor] generated kernel calls empty_strided_cpu with a negative stride → Expected result 0#188227的本质是Inductor 在代码生成阶段推导中间张量的 stride 布局时算出了负数 stride并直接喂给torch.empty_strided要求 stride ≥ 0触发Expected result 0断言崩溃。它常暴露在 CPU fallback 内核路径根因在 Inductor 的 layout 推断逻辑而非你的模型写错。 应对应急torch._dynamo.config.suppress_errors True让编译失败回退 eager不崩绕过在触发步骤后加.contiguous()切断负 stride 传播关dynamicTrue避开动态形状分支换backendeager根本升级 PyTorch 到修复版本CI 中对触发模型暂时禁用 inductor。 记住empty_strided永远只接受非负 stride任何「看起来反向」的视图都应通过transpose/flip/as_strided的合法路径获得Inductor 不该把负号直接传给它。