@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 100c7b65e7
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 4, 17:30 UTC

[Platform] Fix RISC-V platform detection (lscpu parsing + non-NUMA meminfo) (#40427)

Signed-off-by: liuyudong <liuyudong@iscas.ac.cn>

lyd1992 committed Apr 24, 2026 at 12:33 UTC 100c7b65e7579c8caf4ee0b04a6410b2796b905c
3 files changed +37 -8
setup.py
+3 -1
@@ -927,7 +927,9 @@ def get_vllm_version() -> str:
927 elif _is_tpu():
928 version += f"{sep}tpu"
929 elif _is_cpu():
930 - if envs.VLLM_TARGET_DEVICE == "cpu":
930 + # Check the local VLLM_TARGET_DEVICE (may be set by auto-detect above),
931 + # not envs.VLLM_TARGET_DEVICE, so CPU-only hosts still get `+cpu`.
932 + if VLLM_TARGET_DEVICE == "cpu":
933 version += f"{sep}cpu"
934 elif _is_xpu():
935 version += f"{sep}xpu"
vllm/platforms/__init__.py
-1
@@ -177,7 +177,6 @@ def cpu_platform_plugin() -> str | None:
177 logger.debug(
178 "Confirmed CPU platform is available because the machine is MacOS."
179 )
180 -
180 except Exception as e:
181 logger.debug("CPU platform is not available because: %s", str(e))
182
vllm/utils/cpu_resource_utils.py
+34 -6
@@ -87,7 +87,13 @@ def get_memory_node_info(node_id: int = 0) -> MemoryNodeInfo:
87
88 meminfo_path = f"/sys/devices/system/node/node{node_id}/meminfo"
89 if not os.path.exists(meminfo_path):
90 - raise RuntimeError(f"{meminfo_path} doesn't exit.")
90 + # Non-NUMA systems (e.g. many RISC-V boards) don't expose per-node
91 + # meminfo. Fall back to system-wide numbers from psutil.
92 + vm = psutil.virtual_memory()
93 + return MemoryNodeInfo(
94 + total_memory=vm.total,
95 + available_memory=vm.available,
96 + )
97
98 meminfo = {}
99 with open(meminfo_path) as f:
@@ -147,19 +153,36 @@ def get_visible_memory_node() -> list[int]:
153
154
155 @cache
156 +def _synthesize_cpu_list() -> list[LogicalCPUInfo]:
157 + """Synthesize a flat CPU list: each logical CPU is its own core on
158 + NUMA node 0. Used when lscpu output is unavailable or unparsable
159 + (e.g. macOS, RISC-V)."""
160 + cpu_count = os.cpu_count()
161 + assert cpu_count
162 + return [LogicalCPUInfo(i, i, 0) for i in range(cpu_count)]
163 +
164 +
165 def _get_cpu_list() -> list[LogicalCPUInfo]:
166 if platform.system() == "Darwin":
167 # For MacOS, no user-level CPU affinity and SMT, return all CPUs
153 - cpu_count = os.cpu_count()
154 - assert cpu_count
155 - return [LogicalCPUInfo(i, i, 0) for i in range(cpu_count)]
168 + return _synthesize_cpu_list()
169
170 lscpu_output = subprocess.check_output(
171 "lscpu --json --extended=CPU,CORE,NODE --online", shell=True, text=True
172 )
173
161 - # For platform without NUMA, replace '-' to '0'
162 - lscpu_output = re.sub(r'"node":\s*-\s*(,|\n)', r'"node": 0\1', lscpu_output)
174 + # For platforms without NUMA, map bare `-` node to 0 so non-NUMA
175 + # systems keep the existing behavior from #39781.
176 + lscpu_output = re.sub(r'"node":\s*-\s*(,|\n|\})', r'"node": 0\1', lscpu_output)
177 +
178 + # On some architectures (notably RISC-V), lscpu also emits bare `-`
179 + # for cpu/core. Quote them so the JSON parses; they will decode to
180 + # -1 and be filtered out below, triggering the synthesized fallback.
181 + lscpu_output = re.sub(
182 + r'("(?:cpu|core)":\s*)-\s*(,|\n|\})',
183 + r'\1"-"\2',
184 + lscpu_output,
185 + )
186
187 logical_cpu_list: list[LogicalCPUInfo] = json.loads(
188 lscpu_output, object_hook=LogicalCPUInfo.json_decoder
@@ -170,4 +193,9 @@ def _get_cpu_list() -> list[LogicalCPUInfo]:
193 x for x in logical_cpu_list if -1 not in (x.id, x.physical_core, x.numa_node)
194 ]
195
196 + # If lscpu returned no valid entries (e.g. RISC-V where all fields
197 + # are bare `-`), fall back to synthesized topology.
198 + if not logical_cpu_list:
199 + logical_cpu_list = _synthesize_cpu_list()
200 +
201 return logical_cpu_list