Prhub

#43270 [Misc][NUMA] Auto-bind to PCT priority cores on DGX B300 + widen EngineCore across shard NUMA nodes

原始 PR 作者 vadiklyutiy 合并时间 2026-05-29 10:07 文件变更 3 提交数 7 评论 23 代码增减 +622 / -33

执行摘要

EngineCore 跨 NUMA 节点绑定 + PCT 自动绑定

在双路 NUMA 系统中,EngineCore 作为所有 TP/PP Worker 的父进程,之前只绑定到首个 GPU 所在的 NUMA 节点,导致其 Cpus_allowed 受限。当 Worker 分布在另一个 NUMA 节点时,numactl --physcpubind 会验证每个请求的 CPU 属于父进程集合,从而引发 'cpu argument ... is out of range' 错误。此外,DGX B300 的 Xeon 6776P 支持 PCT 技术,部分核心频率可提升至 4.6 GHz(约 2.3 GHz 的两倍),但操作系统默认调度无法自动利用,需要显式绑定才能获得性能优势。

建议精读此 PR,特别是 PCT 自动检测的巧妙设计(无 root 权限利用 acpi_cppc/highest_perf 和 CPUID 模式)以及 EngineCore 与 Worker 绑定分离的架构决策。该 PR 展示了为特殊硬件(DGX B300)进行零配置优化的良好实践。

讨论亮点

Harry-Chen: "I have some concerns regarding only detecting the specific model 6776P, since there are more models supporting PCT." 作者随后扩展支持 6774P 和 6962P,并采用 fail closed 策略。
Harry-Chen: "IIUC, the HP cores can be adjusted dynamically. So hardcoding numbers will not work." 作者解释目前内核不支持动态查询,且生产环境无法使用 intel-speed-select,故使用静态 SKU 表。
Harry-Chen: "I think we can make it return list[int], which is more natural for downstream consumers." 作者改为返回 list[int]
Harry-Chen: "Please be exhaustive and defensive. If kind is neither EngineCore nor Worker, we need to throw some error." 作者添加了警告处理。
gemini-code-assist[bot]: 指出多节点 DP 中 data_parallel_index 可能超出本地 numa_bind_nodes 长度。此问题未在提交中得到明确修复(部分缓解),需后续关注。

实现拆解

  1. 新增 PCT 检测模块vllm/utils/numa_utils.py):定义 _PctSku 命名元组和 _PCT_CAPABLE_SKUS 字典,映射已知 PCT SKU(6776P/6774P/6962P)到其 highest_perfpriority_stride。新增 _pct_sku_config() 缓存函数,读取 /proc/cpuinfo/sys/devices/system/cpu/cpu0/acpi_cppc/highest_perf,若匹配 SKU 表则返回配置,否则返回 None。新增 _maybe_get_pct_cpu_binding(numa_nodes) 函数,对指定的 NUMA 节点,计算其 cpulist 并过滤出 PCT 优先级核心(cpu_id % stride in (0, 1)),返回合并后的 CPU 列表。
  2. 重构 _get_numactl_args 为 Worker 和 EngineCore 专用函数:原 _get_numactl_args 承担两种绑定逻辑,现拆分为 _get_numactl_worker_args(根据显式 numa_bind_cpus 或自动 PCT 节点绑定)和 _get_enginecore_numa_nodes(计算 DP shard 覆盖的所有 NUMA 节点)。configure_subprocess 根据 process_kind 调用对应函数:若为 "EngineCore",使用 _get_enginecore_numa_nodes 生成 --cpunodebind=... --membind=... 参数覆盖整个 shard 的节点,从而扩大其 cpus_allowed
  3. PCT 绑定集成到 Worker 绑定_get_numactl_worker_args 内部调用 _maybe_get_pct_cpu_binding,若返回非空列表则使用 PCT 核心列表生成 --physcpubind=... --membind=...;否则回退到显式的 --numa-bind-cpus--cpunodebind。优先级:显式 --numa-bind-cpus > PCT 自动检测 > --cpunodebind 节点绑定。
  4. 更新配置文档和测试:在 vllm/config/parallel.py 中更新 numa_bind 的文档字符串,说明 PCT 自动绑定行为。tests/utils_/test_numa_utils.py 新增全套 PCT 测试,包括自动启用的 fixture _disable_pct_by_default(避免本地环境干扰)和 _patch_pct_gates helper 以模拟不同 SKU 和 highest_perf 场景。测试覆盖了 SKU 匹配、性能不匹配、节点 cpulist 差异等情况。
文件 模块 状态 重要度
vllm/utils/numa_utils.py NUMA 工具 modified 8.84
tests/utils_/test_numa_utils.py 单元测试 modified 7.52
vllm/config/parallel.py 并行配置 modified 4.27

关键符号

_PctSku _pct_sku_config _maybe_get_pct_cpu_binding _get_numactl_worker_args _get_enginecore_numa_nodes _disable_pct_by_default _patch_pct_gates

关键源码片段

vllm/utils/numa_utils.py core-logic

核心变更文件,包含 PCT 自动检测、EngineCore 绑定拓宽和 Worker 绑定重构的所有逻辑。

# _PctSku: 每个 SKU 的配置元组
# highest_perf: CPPC 最大性能比(100MHz 为单位)
# priority_stride: 每个优先级组包含的逻辑 CPU 数
class _PctSku(NamedTuple):
    highest_perf: int
    priority_stride: int# 已知 PCT 能力的 Granite Rapids SKU 配置表
# 字典结构 : {model_name: _PctSku}
_PCT_CAPABLE_SKUS: dict[str, _PctSku] = {
    "6776P": _PctSku(highest_perf=46, priority_stride=16), # 已实测 DGX B300
    "6774P": _PctSku(highest_perf=46, priority_stride=16), # 按 Intel ARK 推测
    "6962P": _PctSku(highest_perf=44, priority_stride=18), # 按 Intel ARK 推测
}# 系统文件路径,用于检测 PCT 能力
_PCT_HIGHEST_PERF_PATH = "/sys/devices/system/cpu/cpu0/acpi_cppc/highest_perf"
_PROC_CPUINFO_PATH = "/proc/cpuinfo"
​
​
@cache
def _pct_sku_config() -> _PctSku | None:
    """检测当前系统是否为已知 PCT 平台,并返回对应的 SKU 配置。    读取 /proc/cpuinfo 和 /sys/.../highest_perf,如果匹配 SKU 表
    且 highest_perf 一致,则返回配置,否则返回 None(fail closed)。
    """
    # ... 打开文件并比较的逻辑,详见源码
tests/utils_/test_numa_utils.py test-coverage

新增 PCT 相关测试,包括 mock 文件系统的辅助函数和覆盖所有 SKU 的测试用例,确保检测逻辑正确。

@pytest.fixture(autouse=True)
def _disable_pct_by_default(monkeypatch):
    """自动禁用 PCT 检测,避免本地环境干扰。"""
    from io import StringIO
    real_open = open
​
    def _no_pct_open(path, *args, **kwargs):
        # 返回一个不匹配任何 SKU 的 cpuinfo
        if path == numa_utils._PROC_CPUINFO_PATH:
            return StringIO("processor\t: 0\nmodel name\t: Generic Test CPU\n")
        # 使 highest_perf 读取抛出 OSError
        if path == numa_utils._PCT_HIGHEST_PERF_PATH:
            raise OSError("PCT disabled by autouse fixture")
        return real_open(path, *args, **kwargs)
​
    monkeypatch.setattr("builtins.open", _no_pct_open)
    numa_utils._pct_sku_config.cache_clear()
    yield
    numa_utils._pct_sku_config.cache_clear()
​
​
def _patch_pct_gates(
    monkeypatch,
    *,
    model_match: bool,
    highest_perf: int | None,
    cpulist: str | None = "0-31,64-95",
    cpulist_by_node: dict[int, str | None] | None = None,
    sku: str = "6776P",
):
    """覆盖系统文件访问,模拟指定 SKU 和 highest_perf 的 PCT 环境。"""
    import pathlib
    from io import StringIO
    import regex as re
​
    cpuinfo = (
        f"processor\t: 0\nmodel name\t: Intel(R) Xeon(R) Platinum {sku} CPU @ 2.40GHz\n"
        if model_match
        else "processor\t: 0\nmodel name\t: Intel(R) Xeon(R) Platinum 8480+\n"
    )
    real_open = open
​
    def fake_open(path, *args, **kwargs):
        if path == numa_utils._PROC_CPUINFO_PATH:
            return StringIO(cpuinfo)
        if path == numa_utils._PCT_HIGHEST_PERF_PATH:
            if highest_perf is None:
                raise OSError("missing")
            return StringIO(f"{highest_perf}\n")
        return real_open(path, *args, **kwargs)
​
    # ... 类似地 mock pathlib.Path.read_text 返回 cpulist
    monkeypatch.setattr("builtins.open", fake_open)
    # ... 继续设置

评论区精华

EngineCore 特殊绑定处理 设计

gemini-code-assist[bot] 指出如果 EngineCore 也绑定到特定 CPU 会限制子进程;Harry-Chen 最初没有理解,后来明白。

结论:最终采用 EngineCore 跨 shard NUMA 节点绑定,使用 `_get_enginecore_numa_nodes` 生成参数。 · 已解决

PCT SKU 支持范围 设计

Harry-Chen 要求支持所有 PCT SKU 而不是仅 6776P。

结论:作者添加 6774P 和 6962P,采用失败关闭策略(fail closed)。 · 已解决

返回类型建议 style

Harry-Chen 建议 `_maybe_get_pct_cpu_binding` 返回 `list[int]` 更自然。

结论:作者改为返回 `list[int]`。 · 已解决

多节点 DP 潜在问题 正确性

gemini-code-assist[bot] 指出全局 DP rank (`data_parallel_index`) 在 `_get_enginecore_numa_nodes` 中 fallback 可能导致 NUMA 节点索引越界。

结论:作者未明确回复,相关逻辑可能部分缓解但不彻底,需后续关注。 · unresolved

风险与影响

PCT 检测依赖固定 sysfs 路径和 CPUID 格式,若未来硬件或内核变更可能失效;但设计为 fail closed(不启用 PCT),不影响正确性,仅可能损失性能。EngineCore 绑定放宽到跨 NUMA 节点可能增加内存跨 NUMA 访问延迟,但 EngineCore 不执行计算,影响甚微。多节点 DP 场景中,data_parallel_index 的 fallback 可能导致 NUMA 索引越界(如 gemini 所述),当前代码是否彻底修复尚不明确,属残留风险。

正面影响:所有启用 --numa-bind 的用户在 DGX B300 上自动获得 PCT 绑定,性能提升显著(吞吐量 +64%,TPOT -46%);修复了跨 NUMA 节点 DP 场景下 Worker 启动失败的 bug。负面影响:无,行为向后兼容,仅在用户显式开启 --numa-bind 且未指定自定义 CPU 绑定时生效。团队需维护 SKU 表,但 fail closed 机制降低了维护负担。测试覆盖充分(+338 行测试),回归风险低。

跨 NUMA 节点 DP 潜在索引越界 PCT 检测依赖硬件特征且需维护 SKU 表 EngineCore 绑定放宽可能影响内存局部性(但影响小)

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论