Skip to content

Commit b2a6833

Browse files
tamirhemoclaude
andcommitted
fix: use available_parallelism for container-aware thread detection
num_cpus::get_physical() reads /proc/cpuinfo which is not namespaced in containers — it always returns the host's physical core count, ignoring cgroup CPU quotas (docker --cpus, K8s resources.limits.cpu). Use min(available_parallelism, get_physical()) instead: - available_parallelism reads cgroup v1/v2 quota files, so it respects container CPU limits - get_physical caps to avoid SMT oversubscription on bare metal - RAYON_NUM_THREADS still overrides everything Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c724803 commit b2a6833

1 file changed

Lines changed: 13 additions & 5 deletions

File tree

slop/crates/futures/src/rayon.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ static GLOBAL_POOL: OnceLock<()> = OnceLock::new();
1111

1212
/// Initialize the rayon global thread pool.
1313
///
14-
/// Defaults to physical core count when `RAYON_NUM_THREADS` is not set,
15-
/// because SMT siblings cause excessive crossbeam work-stealing contention
16-
/// that outweighs the parallelism benefit.
14+
/// Thread count selection (when `RAYON_NUM_THREADS` is not set):
15+
/// - Uses `min(available_parallelism, physical_cores)` to avoid both
16+
/// SMT oversubscription (crossbeam contention) and container overcommit.
17+
/// - `available_parallelism` respects cgroup CPU quotas (K8s `resources.limits.cpu`,
18+
/// `docker --cpus=N`) and affinity masks, so this works in containers.
19+
/// - `get_physical()` caps it to avoid SMT siblings on bare metal.
1720
///
1821
/// Must be called before any rayon work (par_iter, spawn, etc.) to take effect.
1922
/// Safe to call multiple times — only the first call configures the pool.
@@ -22,9 +25,14 @@ pub fn init_global_pool() {
2225
let mut builder = rayon::ThreadPoolBuilder::new().panic_handler(panic_handler);
2326

2427
if std::env::var("RAYON_NUM_THREADS").is_err() {
28+
let cgroup_aware =
29+
std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
2530
let physical = num_cpus::get_physical();
26-
tracing::info!("rayon pool: using {physical} threads (physical cores)");
27-
builder = builder.num_threads(physical);
31+
let threads = cgroup_aware.min(physical);
32+
tracing::info!(
33+
"rayon pool: using {threads} threads (available_parallelism={cgroup_aware}, physical={physical})"
34+
);
35+
builder = builder.num_threads(threads);
2836
}
2937

3038
builder.build_global().ok();

0 commit comments

Comments
 (0)