deps: prefer cpuinfo_cur_freq over scaling_cur_freq in uv_cpu_info()#62018
deps: prefer cpuinfo_cur_freq over scaling_cur_freq in uv_cpu_info()#62018RajeshKumar11 wants to merge 1 commit intonodejs:mainfrom
Conversation
On Linux, uv_cpu_info() reads the current CPU frequency for each core from /sys/devices/system/cpu/cpuN/cpufreq/scaling_cur_freq. On some modern AMD CPUs (EPYC, Ryzen 9000 series) running inside containers, reading scaling_cur_freq triggers an ACPI/SMM round-trip that can take ~20ms per core, making os.cpus() take 500-600ms on a 32-core system. The file cpuinfo_cur_freq exposes the same current frequency information but reads from a hardware-cached register, avoiding the costly ACPI round-trip. Prefer it when available and fall back to scaling_cur_freq for systems that do not expose cpuinfo_cur_freq. Fixes: nodejs#61998
|
Review requested:
|
|
Changes to libuv should be proposed upstream (https://github.com/libuv/libuv). |
richardlau
left a comment
There was a problem hiding this comment.
This would need to be changed in upstream libuv.
|
Thank you for the feedback @Renegade334 and @richardlau. Understood — the correct path is to land this upstream in libuv first. I'm opening a PR at https://github.com/libuv/libuv now. For context: the libuv issue (libuv/libuv#4098) was previously closed as NOT_PLANNED, with the maintainers' position being that I'll update this PR with the libuv upstream PR link once it's open. |
|
Upstream PR is now open: libuv/libuv#5036 I've submitted the same fix to libuv's |
Out of curiosity, why is that? If it's to compute the available number of cores, is availableParalelism faster? |
|
@saghul Good question — The reason I still think fixing
That said, I agree The upstream fix is at libuv/libuv#5036. |
|
Closing this PR; feel free to continue discussion in the appropriate venue. |
Summary
uv_cpu_info()on Linux, reading/sys/devices/system/cpu/cpuN/cpufreq/scaling_cur_freqfor each CPU core triggers a slow ACPI/SMM round-trip on some modern AMD CPUs inside containers — up to ~20ms per core, makingos.cpus()take 500–600ms on a 32-core systemcpuinfo_cur_freqexposes the same current-frequency information but reads from a hardware-cached register, avoiding the ACPI round-tripcpuinfo_cur_freqand falls back toscaling_cur_freqfor systems that do not expose itRoot cause
uv_cpu_info()indeps/uv/src/unix/linux.citerates over every online CPU and opens/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freqsequentially. On AMD EPYC and Ryzen 9000-series CPUs running inside a Linux container, eachopen()+read()of that file causes the kernel cpufreq driver to issue an ACPI call into System Management Mode (SMM) to query the live clock frequency. SMM is a privileged firmware context that suspends all other CPU execution while it runs; inside a container the latency is amplified and regularly reaches 20 ms per core.With 32 cores: 32 × 20 ms = ~640 ms spent inside a single
os.cpus()call.The regression was introduced when libuv switched from
cpuinfo_cur_freqtoscaling_cur_freq(libuv issue libuv/libuv#4098). The libuv project closed the upstream issue as NOT_PLANNED.Confirmed by comparing
node:20.2.0-alpine(19 ms) vsnode:20.3.0-alpine(356 ms) on the same AMD host.Fix
deps/uv/src/unix/linux.c— in the per-CPU frequency loop starting around line 1884:cpuinfo_cur_freqfirst; it is a cached readout of the actual hardware frequency reported by the CPU's own registers (via/sys/bus/cpu/drivers/acpi-cpufreqor equivalent) and does not trigger SMMscaling_cur_freq— identical behaviour to todayThe change is purely a path-preference inside the existing
if (fp == NULL) continue;fallback pattern already used in the function, so it is safe on all platforms that currently work.Test
os.cpus()correctness (structure, types,speedfield) is already covered bytest/parallel/test-os.js. No new test is added — a timing assertion for this fix would be flaky in CI and the slow path requires a specific AMD CPU model inside a Linux container.Related
uv_cpu_infolibuv/libuv#4098 (closed NOT_PLANNED)process.report.getReportslower from 20.2.0 to 20.3.0 on Linux #48831Fixes: #61998