Prhub

#39361 Fix NUMA binding on non-CDMM Grace-Blackwell systems

原始 PR 作者 soodoshll 合并时间 2026-04-09 15:36 文件变更 1 提交数 4 评论 3 代码增减 +23 / -1

执行摘要

修复非 CDMM Grace-Blackwell 系统上 NUMA 绑定失败问题。

根据PR描述和相关讨论(#38635),在非CDMM Grace-Blackwell系统上,每个GPU的HBM作为独立的NUMA节点暴露,这些节点只有内存没有CPU(例如节点2、10、18、26)。nvmlDeviceGetNumaNodeId()返回这些仅内存节点,导致numactl --cpunodebind因“Invalid argument”而失败。需要修复以支持此类硬件拓扑。

该PR值得精读,特别是对于需要处理异构NUMA架构的开发者。关注_numa_node_has_cpus方法的实现,它展示了如何通过sysfs检测NUMA节点属性,以及回退机制的设计决策。

讨论亮点

review评论较少,主要确认修复的有效性:

  • @Harry-Chen 表示:“我在GB200上运行了测试,但只在启用了CDDM的节点上。所以非常感谢这个修复!”
  • @ywang96 和 @Harry-Chen 均批准了PR。
  • gemini-code-assist[bot] 的评论总结了变更内容,指出没有反馈需要提供。

实现拆解

  1. 修改NUMA节点检测逻辑:在vllm/platforms/cuda.pyget_device_numa_node方法中,首先尝试通过NVML获取NUMA节点ID,然后调用新增的_numa_node_has_cpus方法检查该节点是否包含CPU。如果包含,直接返回该节点;否则,记录调试日志并回退到原有的CPU亲和性检测路径。
  2. 新增辅助方法:添加_numa_node_has_cpus方法,通过读取/sys/devices/system/node/node{node_id}/cpulist文件内容是否为空来判断NUMA节点是否有CPU。如果文件读取失败或内容为空,则返回False。
  3. 日志增强:在回退到CPU亲和性检测时,添加调试日志说明原因,便于问题诊断。
  4. 测试与验证:PR描述中提到已在非CDMM GB200服务器上测试通过,但未包含测试文件变更。
文件 模块 状态 重要度
vllm/platforms/cuda.py 平台抽象 modified 6.59
vllm/platforms/cuda.py core-logic

这是唯一修改的文件,包含了 NUMA 节点检测的核心逻辑变更,直接影响 GPU 设备的 NUMA 绑定行为。

@classmethod
@with_nvml_context
def get_device_numa_node(cls, device_id: int = 0) -> int | None:
    """Get the NUMA node ID for a GPU device."""
    physical_device_id = cls.device_id_to_physical_device_id(device_id)
    handle = pynvml.nvmlDeviceGetHandleByIndex(physical_device_id)
​
    try:
        numa_node = pynvml.nvmlDeviceGetNumaNodeId(handle)
        if cls._numa_node_has_cpus(numa_node):
            return numa_node # 如果NUMA节点有CPU,直接返回
        # 在非CDMM Grace-Blackwell系统(如GB200)上,每个GPU的HBM
        # 是一个没有CPU的独立NUMA节点。回退到基于CPU亲和性的检测以找到最近的CPU节点。
        logger.debug(
            "NUMA node %d for GPU %d has no CPUs (non-CDMM topology), "
            "falling back to CPU-affinity-based detection",
            numa_node,
            device_id,
        )
    except Exception:
        pass # 如果NVML调用失败,继续尝试CPU亲和性检测
​
    try:
        cpu_ids = cls._get_device_cpu_affinity(handle)
        if cpu_ids:
            numa_node = cls._get_numa_node_for_cpu(cpu_ids[0])
            if numa_node is not None:
                logger.debug(
                    "Determined NUMA node %d for GPU %d via CPU affinity",
                    numa_node,
                    device_id,
                )
                return numa_node
    except Exception as e:
        logger.warning("Failed to get NUMA node for GPU %d: %s", device_id, e)
​
    return None # 如果所有方法都失败,返回None@classmethod
def _numa_node_has_cpus(cls, node_id: int) -> bool:
    """检查NUMA节点是否有任何CPU分配给它。"""
    from pathlib import Path
​
    cpulist_file = Path(f"/sys/devices/system/node/node{node_id}/cpulist")
    try:
        return cpulist_file.read_text().strip() != "" # 文件非空表示有CPU
    except (OSError, ValueError):
        return False # 文件读取失败或不存在时,假设没有CPU

关键符号

get_device_numa_node _numa_node_has_cpus

评论区精华

修复有效性确认 正确性

@Harry-Chen 确认在 GB200 上测试过,但只在 CDDM 启用节点,感谢修复。

结论:修复被认可,适用于非 CDMM 系统。 · 已解决

风险与影响

  1. 回归风险:修改了核心的NUMA节点检测逻辑,如果_numa_node_has_cpus方法实现有误(如文件路径错误或异常处理不充分),可能导致在所有系统上错误地回退到CPU亲和性检测,影响性能或正确性。
  2. 兼容性风险:依赖/sys/devices/system/node/文件系统,在非Linux系统或容器环境中可能不可用,但当前代码通过异常捕获返回False,降低了风险。
  3. 性能影响:新增了文件读取操作,可能引入轻微开销,但仅在NUMA节点检测时调用一次,影响可忽略。
  1. 用户影响:修复了特定硬件(非CDMM Grace-Blackwell系统)上NUMA绑定失败的问题,使vLLM能在这些系统上正常运行,提升了硬件兼容性。
  2. 系统影响:确保NUMA感知的内存分配和进程绑定正常工作,可能优化内存访问性能。
  3. 团队影响:为后续支持类似异构NUMA拓扑的硬件提供了参考实现。
核心路径变更 依赖外部文件系统

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

执行摘要

  • 一句话:修复非CDMM Grace-Blackwell系统上NUMA绑定失败问题。
  • 推荐动作:该PR值得精读,特别是对于需要处理异构NUMA架构的开发者。关注_numa_node_has_cpus方法的实现,它展示了如何通过sysfs检测NUMA节点属性,以及回退机制的设计决策。

功能与动机

根据PR描述和相关讨论(#38635),在非CDMM Grace-Blackwell系统上,每个GPU的HBM作为独立的NUMA节点暴露,这些节点只有内存没有CPU(例如节点2、10、18、26)。nvmlDeviceGetNumaNodeId()返回这些仅内存节点,导致numactl --cpunodebind因“Invalid argument”而失败。需要修复以支持此类硬件拓扑。

实现拆解

  1. 修改NUMA节点检测逻辑:在vllm/platforms/cuda.pyget_device_numa_node方法中,首先尝试通过NVML获取NUMA节点ID,然后调用新增的_numa_node_has_cpus方法检查该节点是否包含CPU。如果包含,直接返回该节点;否则,记录调试日志并回退到原有的CPU亲和性检测路径。
  2. 新增辅助方法:添加_numa_node_has_cpus方法,通过读取/sys/devices/system/node/node{node_id}/cpulist文件内容是否为空来判断NUMA节点是否有CPU。如果文件读取失败或内容为空,则返回False。
  3. 日志增强:在回退到CPU亲和性检测时,添加调试日志说明原因,便于问题诊断。
  4. 测试与验证:PR描述中提到已在非CDMM GB200服务器上测试通过,但未包含测试文件变更。

关键文件:

  • vllm/platforms/cuda.py(模块 平台抽象;类别 source;类型 core-logic;符号 get_device_numa_node, _numa_node_has_cpus): 这是唯一修改的文件,包含了NUMA节点检测的核心逻辑变更,直接影响GPU设备的NUMA绑定行为。

关键符号:get_device_numa_node, _numa_node_has_cpus

关键源码片段

vllm/platforms/cuda.py

这是唯一修改的文件,包含了NUMA节点检测的核心逻辑变更,直接影响GPU设备的NUMA绑定行为。

@classmethod
@with_nvml_context
def get_device_numa_node(cls, device_id: int = 0) -> int | None:
    """Get the NUMA node ID for a GPU device."""
    physical_device_id = cls.device_id_to_physical_device_id(device_id)
    handle = pynvml.nvmlDeviceGetHandleByIndex(physical_device_id)
​
    try:
        numa_node = pynvml.nvmlDeviceGetNumaNodeId(handle)
        if cls._numa_node_has_cpus(numa_node):
            return numa_node # 如果NUMA节点有CPU,直接返回
        # 在非CDMM Grace-Blackwell系统(如GB200)上,每个GPU的HBM
        # 是一个没有CPU的独立NUMA节点。回退到基于CPU亲和性的检测以找到最近的CPU节点。
        logger.debug(
            "NUMA node %d for GPU %d has no CPUs (non-CDMM topology), "
            "falling back to CPU-affinity-based detection",
            numa_node,
            device_id,
        )
    except Exception:
        pass # 如果NVML调用失败,继续尝试CPU亲和性检测
​
    try:
        cpu_ids = cls._get_device_cpu_affinity(handle)
        if cpu_ids:
            numa_node = cls._get_numa_node_for_cpu(cpu_ids[0])
            if numa_node is not None:
                logger.debug(
                    "Determined NUMA node %d for GPU %d via CPU affinity",
                    numa_node,
                    device_id,
                )
                return numa_node
    except Exception as e:
        logger.warning("Failed to get NUMA node for GPU %d: %s", device_id, e)
​
    return None # 如果所有方法都失败,返回None@classmethod
def _numa_node_has_cpus(cls, node_id: int) -> bool:
    """检查NUMA节点是否有任何CPU分配给它。"""
    from pathlib import Path
​
    cpulist_file = Path(f"/sys/devices/system/node/node{node_id}/cpulist")
    try:
        return cpulist_file.read_text().strip() != "" # 文件非空表示有CPU
    except (OSError, ValueError):
        return False # 文件读取失败或不存在时,假设没有CPU

评论区精华

review评论较少,主要确认修复的有效性:

  • @Harry-Chen 表示:“我在GB200上运行了测试,但只在启用了CDDM的节点上。所以非常感谢这个修复!”
  • @ywang96 和 @Harry-Chen 均批准了PR。
  • gemini-code-assist[bot] 的评论总结了变更内容,指出没有反馈需要提供。

  • 修复有效性确认 (correctness): 修复被认可,适用于非CDMM系统。

风险与影响

  • 风险:1. 回归风险:修改了核心的NUMA节点检测逻辑,如果_numa_node_has_cpus方法实现有误(如文件路径错误或异常处理不充分),可能导致在所有系统上错误地回退到CPU亲和性检测,影响性能或正确性。
    2. 兼容性风险:依赖/sys/devices/system/node/文件系统,在非Linux系统或容器环境中可能不可用,但当前代码通过异常捕获返回False,降低了风险。
    3. 性能影响:新增了文件读取操作,可能引入轻微开销,但仅在NUMA节点检测时调用一次,影响可忽略。
  • 影响:1. 用户影响:修复了特定硬件(非CDMM Grace-Blackwell系统)上NUMA绑定失败的问题,使vLLM能在这些系统上正常运行,提升了硬件兼容性。
    2. 系统影响:确保NUMA感知的内存分配和进程绑定正常工作,可能优化内存访问性能。
    3. 团队影响:为后续支持类似异构NUMA拓扑的硬件提供了参考实现。
  • 风险标记:核心路径变更, 依赖外部文件系统

关联脉络

  • PR #38635 未知: PR描述中提及相关讨论在此PR中,可能涉及NUMA绑定问题的前期探讨。

参与讨论