执行摘要
- 一句话:移除PyTorch named tensors依赖,避免向上兼容风险
- 推荐动作:建议阅读核心文件
tensor_naming.py 的变更,理解如何用私有属性模拟命名语义。该模式可推广到其他需要绕过废弃 API 的场景。同时也值得查看 review 讨论中关于原地修改与非连续张量的设计权衡。
功能与动机
PyTorch将在未来版本中移除named tensors特性(参见PyTorch #173895)。为了避免SGLang仓库因此中断,PR清除了所有该特性的使用。作者强调使用仅局限于调试工具和关联测试,风险极低。
实现拆解
- 在
tensor_naming.py 中定义了三个辅助函数:get_dim_names(读取)、apply_dim_names(赋予)和 without_dim_names(移除),将维度名称存储为张量的 _dim_names 私有属性。
- 在涉及维度名称操作的多个 executor 文件(如
unsharder/executor.py、token_aligner/smart/executor.py、reorderer/executor.py、bundle_comparator.py 等)中,将 tensor.names 替换为 get_dim_names,将 tensor.refine_names() 替换为 apply_dim_names,将 tensor.rename(None) 替换为 without_dim_names。
- 同步更新了关联的测试文件,确保断言中张量比较时调用
without_dim_names 来消除维度名称影响。
- 根据 review 意见,将
apply_dim_names 从直接修改输入张量改为通过 torch.ops.aten.alias 创建别名后修改,保持非原地语义;同时使用 alias 确保非连续张量的兼容性。
关键文件:
python/sglang/srt/debug_utils/comparator/dims_spec/tensor_naming.py(模块 张量命名;类别 source;类型 core-logic;符号 get_dim_names, apply_dim_names, without_dim_names): 定义核心辅助函数 get_dim_names、apply_dim_names、without_dim_names,是整个 PR 的变更基础。
python/sglang/srt/debug_utils/comparator/aligner/unsharder/executor.py(模块 去分片;类别 source;类型 core-logic): 在核心 unshard 逻辑中全面替换 named tensor 操作为新辅助函数,体现 PR 的主要调用模式。
python/sglang/srt/debug_utils/comparator/aligner/token_aligner/smart/executor.py(模块 令牌对齐;类别 source;类型 core-logic): 在 token aligner 中替换 strip_dim_names 的使用,展示 BS→T 折叠等场景的适配。
python/sglang/srt/debug_utils/comparator/bundle_comparator.py(模块 束比较;类别 source;类型 core-logic): 在顶层束比较入口处理对齐结果的维度名称清理,使用 without_dim_names 替换 rename(None)。
test/registered/debug_utils/comparator/aligner/unsharder/test_executor.py(模块 测试;类别 test;类型 test-coverage): 测试文件同步更新,使用 apply_dim_names 和 without_dim_names 验证 unshard 结果。
关键符号:get_dim_names, apply_dim_names, without_dim_names, resolve_dim_by_name
关键源码片段
python/sglang/srt/debug_utils/comparator/dims_spec/tensor_naming.py
定义核心辅助函数 get_dim_names、apply_dim_names、without_dim_names,是整个 PR 的变更基础。
from __future__ import annotations
from typing import Optional
import torch
_DIM_NAMES_ATTR = "_dim_names" # 私有属性名称,用于存储维度名称
def get_dim_names(tensor: torch.Tensor) -> tuple[Optional[str], ...]:
"""获取张量的维度名称。如果未设置名称,则返回全 None 的元组。"""
names = getattr(tensor, _DIM_NAMES_ATTR, None)
if names is not None:
return names
return (None,) * tensor.ndim
def apply_dim_names(tensor: torch.Tensor, dim_names: list[str]) -> torch.Tensor:
"""为张量设置维度名称。返回一个带有名称的新视图,不修改原张量。"""
if tensor.ndim != len(dim_names):
raise ValueError(
f"dims metadata mismatch: tensor has {tensor.ndim} dims (shape {list(tensor.shape)}) "
f"but dims string specifies {len(dim_names)} names {dim_names}. "
f"Please fix the dims string in the dumper.dump() call to match the actual tensor shape."
)
view = torch.ops.aten.alias(tensor) # 创建别名,保留 contiguity 和 strides
view._dim_names = tuple(dim_names) # 在别名上设置私有属性
return view
def without_dim_names(tensor: torch.Tensor) -> torch.Tensor:
"""返回一个没有维度名称的新视图。"""
return torch.ops.aten.alias(tensor) # 别名不会继承 _dim_names
python/sglang/srt/debug_utils/comparator/aligner/unsharder/executor.py
在核心 unshard 逻辑中全面替换 named tensor 操作为新辅助函数,体现 PR 的主要调用模式。
def _apply_unshard(
params: UnsharderParams,
ordered_tensors: list[torch.Tensor],
*,
axis: ParallelAxis,
group_index: int,
) -> tuple[torch.Tensor, list[ReplicatedCheckResult]]:
if isinstance(params, PickParams):
checks = _verify_replicated_group(ordered_tensors, axis=axis, group_index=group_index)
return ordered_tensors[0], checks
if isinstance(params, ConcatParams):
dim: int = resolve_dim_by_name(ordered_tensors[0], params.dim_name)
names = get_dim_names(ordered_tensors[0]) # 获取原始维度名称
result = torch.cat(ordered_tensors, dim=dim)
if names[0] is not None:
result = apply_dim_names(result, list(names)) # 保留名称
return result, []
if isinstance(params, ReduceSumParams):
names = get_dim_names(ordered_tensors[0])
stripped = [without_dim_names(t) for t in ordered_tensors] # 移除名称后再 stack/sum
result = torch.stack(stripped).sum(dim=0)
if names[0] is not None:
result = apply_dim_names(result, list(names))
return result, []
# ...
评论区精华
风险与影响
- 风险:主要风险在于调试工具(debug_utils/comparator)的维度名称传递可能出现遗漏或错误,导致张量对齐或比较失败。但由于所有变更均基于简单的 API 替换,且测试覆盖充分,风险可控。此外,使用私有属性
_dim_names 需要确保在张量别名传播时不丢失,torch.ops.aten.alias 能正确共享数据但不会自动传播属性,因此 without_dim_names 通过不设置 _dim_names 来模拟无名称状态,语义正确。
- 影响:直接影响是清理了废弃 API,保证未来 PyTorch 升级时不会因 named tensors 移除而出错。受影响范围限定在 debug_utils/comparator 模块及其测试,不影响核心推理或训练路径。对于使用调试工具的开发人员,行为保持透明。
- 风险标记:依赖私有属性, 调试工具范围, PyTorch升级兼容
关联脉络
参与讨论