Loading page…
Rust walkthroughs
Loading page…
rayon::current_num_threads relate to the actual thread pool configuration and when might they differ?rayon::current_num_threads returns the number of threads in the currently active thread pool, which defaults to the global thread pool unless you're executing within a custom ThreadPool scope. The function queries the thread pool that owns the current task, so the returned value reflects the actual pool configuration at runtime. However, the value can differ from the number of CPU cores if you've configured a custom thread pool, if you're calling it from outside a Rayon task where no pool is active, or if the global pool was configured with a specific thread count during initialization.
use rayon::current_num_threads;
fn main() {
// By default, global pool uses number of CPU cores
let threads = current_num_threads();
println!("Current threads: {}", threads);
// This equals the number of logical CPUs
let cpus = num_cpus::get();
println!("CPU cores: {}", cpus);
assert_eq!(threads, cpus);
}The default configuration matches the number of logical CPU cores.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
// Configure global pool before any parallel work
ThreadPoolBuilder::new()
.num_threads(4)
.build_global()
.unwrap();
// Now current_num_threads returns 4, not CPU count
rayon::spawn(move || {
println!("Threads in global pool: {}", current_num_threads());
});
rayon::join(
|| println!("Left side"),
|| println!("Right side"),
);
println!("After configuration: {} threads", current_num_threads());
}The global pool can be configured once at startup with a custom thread count.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
// Global pool with 4 threads
ThreadPoolBuilder::new()
.num_threads(4)
.build_global()
.unwrap();
// Create a custom pool with different thread count
let custom_pool = ThreadPoolBuilder::new()
.num_threads(2)
.build()
.unwrap();
// Outside thread pool context
println!("Outside pool: {} threads", current_num_threads());
// Inside global pool
rayon::spawn(move || {
println!("In global pool: {} threads", current_num_threads());
});
// Inside custom pool
custom_pool.spawn(move || {
println!("In custom pool: {} threads", current_num_threads());
});
custom_pool.join(|| {
println!("During install: {} threads", current_num_threads());
});
}current_num_threads reflects the pool context where it's called.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
let pool4 = ThreadPoolBuilder::new().num_threads(4).build().unwrap();
let pool2 = ThreadPoolBuilder::new().num_threads(2).build().unwrap();
// Install pool4 as the current thread pool
pool4.install(|| {
println!("Inside pool4: {} threads", current_num_threads());
// Nested pool2 installation
pool2.install(|| {
println!("Inside pool2: {} threads", current_num_threads());
});
// Back to pool4
println!("Back to pool4: {} threads", current_num_threads());
});
// Outside any pool context
println!("Outside: {} threads", current_num_threads());
}install changes which pool the current code executes in.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
// Without configuring global pool
let pool = ThreadPoolBuilder::new()
.num_threads(8)
.build()
.unwrap();
// Call current_num_threads from main thread (not in Rayon context)
// This still returns the global pool's thread count
println!("From main: {} threads", current_num_threads());
// If global pool not configured, returns CPU count
// This happens because current_num_threads falls back to global
}When called outside any pool, current_num_threads returns the global pool's count.
use rayon::{current_num_threads, ThreadPoolBuilder, join};
fn main() {
let pool = ThreadPoolBuilder::new()
.num_threads(3)
.build()
.unwrap();
pool.install(|| {
println!("Pool has {} threads", current_num_threads());
// join splits work within the same pool
join(
|| println!("Left: {} threads", current_num_threads()),
|| println!("Right: {} threads", current_num_threads()),
);
// Parallel iterator uses same pool
(0..10).into_par_iter().for_each(|i| {
println!("Item {}: {} threads", i, current_num_threads());
});
});
}All Rayon operations within install use the same pool.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
// RAYON_NUM_THREADS environment variable overrides default
// export RAYON_NUM_THREADS=2
// If RAYON_NUM_THREADS=2, this returns 2
println!("Default threads: {}", current_num_threads());
// Programmatic configuration overrides environment
ThreadPoolBuilder::new()
.num_threads(6)
.build_global()
.unwrap();
// Now returns 6
println!("After config: {}", current_num_threads());
}Environment variables can configure the pool before program code.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
let cpus = num_cpus::get();
// Scenario 1: Configured fewer threads than CPUs
ThreadPoolBuilder::new()
.num_threads(2)
.build_global()
.unwrap();
println!("CPUs: {}, Threads: {}", cpus, current_num_threads());
// Threads < CPUs: undersubscribed
// Scenario 2: Configured more threads than CPUs
ThreadPoolBuilder::new()
.num_threads(cpus * 2)
.build_global()
.unwrap();
println!("CPUs: {}, Threads: {}", cpus, current_num_threads());
// Threads > CPUs: oversubscribed
// Why more threads than CPUs?
// - I/O-bound work with blocking operations
// - Mixed CPU and I/O workloads
// - Hiding latency from blocking calls
}Thread count doesn't need to match CPU count for all workloads.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
// Common pattern: dedicated pool for CPU work in async context
let compute_pool = ThreadPoolBuilder::new()
.num_threads(4)
.thread_name(|i| format!("compute-{}", i))
.build()
.unwrap();
let io_pool = ThreadPoolBuilder::new()
.num_threads(8) // More threads for I/O
.thread_name(|i| format!("io-{}", i))
.build()
.unwrap();
compute_pool.install(|| {
println!("Compute pool: {} threads", current_num_threads());
// CPU-intensive work here
});
io_pool.install(|| {
println!("I/O pool: {} threads", current_num_threads());
// I/O-bound work here
});
}Different pools can have different thread counts for different workloads.
use rayon::{current_num_threads, ThreadPoolBuilder, join};
fn main() {
let pool = ThreadPoolBuilder::new()
.num_threads(4)
.build()
.unwrap();
// Case 1: Called outside pool context
println!("Outside: {} threads", current_num_threads());
// Returns global pool count (may be CPU count if not configured)
// Case 2: Global pool configured differently
ThreadPoolBuilder::new()
.num_threads(8)
.build_global()
.unwrap();
let custom_pool = ThreadPoolBuilder::new()
.num_threads(4)
.build()
.unwrap();
println!("Global: {}", current_num_threads()); // 8
custom_pool.install(|| {
println!("Custom: {}", current_num_threads()); // 4
});
println!("After install: {}", current_num_threads()); // 8 (back to global)
// Case 3: Nested installs
let pool_a = ThreadPoolBuilder::new().num_threads(2).build().unwrap();
let pool_b = ThreadPoolBuilder::new().num_threads(3).build().unwrap();
pool_a.install(|| {
println!("Pool A: {}", current_num_threads()); // 2
pool_b.install(|| {
println!("Pool B inside A: {}", current_num_threads()); // 3
});
println!("Back to A: {}", current_num_threads()); // 2
});
}The returned value depends on the current execution context.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
let pool = ThreadPoolBuilder::new()
.num_threads(3)
.thread_name(|i| format!("worker-{}", i))
.build()
.unwrap();
pool.install(|| {
// Thread names help identify in debugging/profiling
let thread_name = std::thread::current().name().unwrap_or("unknown");
println!("Running in thread: {}, pool size: {}",
thread_name, current_num_threads());
(0..6).into_par_iter().for_each(|i| {
let name = std::thread::current().name().unwrap_or("unknown");
println!("Task {} on thread: {}", i, name);
});
});
}Thread names help verify which pool is executing work.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
let pool = ThreadPoolBuilder::new()
.num_threads(2)
.stack_size(1024 * 1024) // 1MB stack per thread
.build()
.unwrap();
pool.install(|| {
println!("Pool has {} threads", current_num_threads());
// Each thread has 1MB stack
});
}Stack size is configured per-thread but doesn't affect thread count.
use rayon::{current_num_threads, ThreadPoolBuilder};
fn main() {
// Debug: print pool configuration at startup
fn debug_pool_config() {
let cpus = num_cpus::get();
let threads = current_num_threads();
println!("CPUs: {}, Pool threads: {}", cpus, threads);
if threads > cpus {
println!("Warning: Oversubscribed (more threads than CPUs)");
} else if threads < cpus {
println!("Info: Undersubscribed (fewer threads than CPUs)");
}
}
debug_pool_config();
// Reconfigure if needed
ThreadPoolBuilder::new()
.num_threads(num_cpus::get())
.build_global()
.unwrap();
debug_pool_config();
}Compare thread count to CPU count for performance analysis.
use rayon::{current_num_threads, ThreadPoolBuilder, join};
fn main() {
let pool = ThreadPoolBuilder::new()
.num_threads(1) // Single-threaded pool
.build()
.unwrap();
pool.install(|| {
println!("Pool threads: {}", current_num_threads());
// Even with 1 thread, join works sequentially
join(
|| println!("Left"),
|| println!("Right"),
);
// Parallel iterator falls back to sequential
(0..10).into_par_iter().for_each(|i| {
println!("Sequential: {}", i);
});
});
}Rayon handles thread pools of any size, including single-threaded.
use rayon::{current_num_threads, ThreadPoolBuilder, spawn};
fn main() {
let pool = ThreadPoolBuilder::new()
.num_threads(4)
.build()
.unwrap();
// spawn uses current pool context
pool.install(|| {
spawn(|| {
println!("Spawned task in pool with {} threads", current_num_threads());
});
});
// spawn outside install uses global pool
spawn(|| {
println!("Global pool: {} threads", current_num_threads());
});
}spawn inherits the current thread pool context.
Core behavior: current_num_threads returns the thread count of the pool executing the current task, falling back to the global pool when called outside a thread pool context.
When values differ:
RAYON_NUM_THREADS overrides defaultKey patterns:
ThreadPoolBuilder::num_threads() to configureinstall() to run code in a specific pool contextPractical guidance:
current_num_threads inside install or parallel context for accurate resultsKey insight: current_num_threads is context-aware and reflects the pool configuration at the point of execution. The value can change during nested install calls, and differs from CPU count when pools are explicitly configured. For production code, explicitly configure the global pool at startup or use custom pools with known thread counts, rather than relying on the default CPU-count heuristic which may not match your workload characteristics.