From ef9d8021d69bdad44cbd7deea6220736da58a808 Mon Sep 17 00:00:00 2001 From: Andrew Budge Date: Wed, 17 Jun 2026 12:35:44 -0600 Subject: [PATCH] perf: avoid sysinfo System::new_all() when only one metric is needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found via the criterion suite: parse/10_simple and parse/1000_simple both cost a flat ~50ms, while the DAG engine builds 10,000 rules in 3ms. A fixed cost independent of workload size pointed at WorkflowConfig::validate(), which called sysinfo::System::new_all() — walking all of /proc (every process, disk, and NIC) — just to read total system memory. Probe only RAM with System::new() + refresh_memory(). total_memory() still returns bytes in sysinfo 0.38, so behavior is unchanged. Same fix applied to executor::available_memory_gb, available_threads (now num_cpus::get()), and process::detect_total_memory_mb. parse/10_simple: 50ms -> 39us. All 890 lib tests pass; fmt + clippy clean. --- crates/oxo-flow-core/src/config.rs | 5 ++++- crates/oxo-flow-core/src/executor/mod.rs | 6 ++++-- crates/oxo-flow-core/src/executor/process.rs | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/oxo-flow-core/src/config.rs b/crates/oxo-flow-core/src/config.rs index 3d49926..c70eba9 100644 --- a/crates/oxo-flow-core/src/config.rs +++ b/crates/oxo-flow-core/src/config.rs @@ -1267,7 +1267,10 @@ impl WorkflowConfig { let system_threads = num_cpus::get() as u32; let system_memory_mb = { use sysinfo::System; - let mut sys = System::new_all(); + // Only memory is needed here; `System::new_all()` would walk all of + // /proc (every process, disk, and network interface) just to read + // total RAM, adding ~50ms to every parse/validate/dry-run/run call. + let mut sys = System::new(); sys.refresh_memory(); sys.total_memory() / 1024 / 1024 }; diff --git a/crates/oxo-flow-core/src/executor/mod.rs b/crates/oxo-flow-core/src/executor/mod.rs index 638fcdc..4c76b7a 100644 --- a/crates/oxo-flow-core/src/executor/mod.rs +++ b/crates/oxo-flow-core/src/executor/mod.rs @@ -17,13 +17,15 @@ mod tests; /// Get available CPU threads for auto-scaling. #[must_use] pub fn available_threads() -> u32 { - System::new_all().cpus().len() as u32 + num_cpus::get() as u32 } /// Get available memory in GB for auto-scaling. #[must_use] pub fn available_memory_gb() -> u64 { - let mut sys = System::new_all(); + // Only memory is needed; `System::new_all()` would also walk every process, + // disk, and network interface in /proc for no reason. + let mut sys = System::new(); sys.refresh_memory(); sys.available_memory() / (1024 * 1024 * 1024) // Convert bytes to GB } diff --git a/crates/oxo-flow-core/src/executor/process.rs b/crates/oxo-flow-core/src/executor/process.rs index 2d15798..3b1eb5e 100644 --- a/crates/oxo-flow-core/src/executor/process.rs +++ b/crates/oxo-flow-core/src/executor/process.rs @@ -300,7 +300,8 @@ fn detect_total_memory_mb() -> u64 { // Primary: sysinfo crate (cross-platform) if let Ok(mb) = std::panic::catch_unwind(|| { use sysinfo::System; - let mut sys = System::new_all(); + // Only memory is needed; avoid `System::new_all()` scanning all of /proc. + let mut sys = System::new(); sys.refresh_memory(); sys.total_memory() / 1024 / 1024 }) && mb > 0