diff --git a/host/src/process/cache.rs b/host/src/process/cache.rs index 972feea..bc81b55 100644 --- a/host/src/process/cache.rs +++ b/host/src/process/cache.rs @@ -54,13 +54,10 @@ pub(super) struct Compiler { } impl Compiler { pub fn new(directory: &Path) -> anyhow::Result> { - Self::with_opt(directory, wasmtime::OptLevel::Speed) - } - fn with_opt(directory: &Path, opt: wasmtime::OptLevel) -> anyhow::Result> { let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); let mut config = wasmtime::Config::new(); config.epoch_interruption(true); - config.cranelift_opt_level(opt); + config.cranelift_opt_level(wasmtime::OptLevel::None); let mut cache = wasmtime::CacheConfig::new(); cache.with_directory(directory); config.cache(Some(wasmtime::Cache::new(cache)?)); @@ -145,6 +142,8 @@ pub(super) fn precompile( ) -> tokio::task::JoinHandle> { tokio::spawn(async move { let mut tasks = tokio::task::JoinSet::new(); + // Leave CPU and file bandwidth for commands during startup. + let slots = Arc::new(tokio::sync::Semaphore::new(2)); for folder in ["/bin", "/usr/local/bin"] { let entries = match root.list(folder).await { Ok(entries) => entries, @@ -155,7 +154,9 @@ pub(super) fn precompile( let root = root.clone(); let compiler = compiler.clone(); let path = format!("{folder}/{}", entry.name); + let slots = slots.clone(); tasks.spawn(async move { + let _permit = slots.acquire().await?; let file = root .open( &path, @@ -190,112 +191,6 @@ mod tests { use crate::exec::{Command, Exec, Options}; use std::{future::Future, sync::atomic::Ordering}; - #[tokio::test(flavor = "multi_thread")] - #[ignore = "Temporary T15 timing probe; remove before review"] - async fn measure_world() { - let bundle = Path::new(env!("CARGO_MANIFEST_DIR")).join("../target/userland/bundle"); - let temp = tempfile::tempdir().unwrap(); - for (index, script) in ["echo hi | tr a-z A-Z", "git --version", "rg --version"] - .into_iter() - .enumerate() - { - let cache = temp.path().join(format!("cache-{index}")); - let start = std::time::Instant::now(); - let world = crate::world::World::open_with_userland( - &temp.path().join(format!("cold-{index}")), - Some(&bundle), - ) - .unwrap(); - let runtime = super::super::Runtime::new_with_cache(world, &cache).unwrap(); - let mut output = |_: crate::exec::Stream, _: &[u8]| {}; - assert_eq!( - runtime - .exec( - Command::Shell(script.into()), - Options::default(), - &mut output - ) - .await - .unwrap(), - 0 - ); - println!( - "profile={} state=cold command={script:?} seconds={:.6}", - if cfg!(debug_assertions) { - "debug" - } else { - "release" - }, - start.elapsed().as_secs_f64() - ); - let count = runtime.precompile().await.unwrap().unwrap(); - println!( - "background commands={count} seconds={:.6}", - start.elapsed().as_secs_f64() - ); - let start = std::time::Instant::now(); - assert_eq!( - runtime - .exec( - Command::Shell(script.into()), - Options::default(), - &mut output - ) - .await - .unwrap(), - 0 - ); - println!( - "state=same-host command={script:?} seconds={:.6}", - start.elapsed().as_secs_f64() - ); - drop(runtime); - let start = std::time::Instant::now(); - let world = crate::world::World::open_with_userland( - &temp.path().join(format!("warm-{index}")), - Some(&bundle), - ) - .unwrap(); - let runtime = super::super::Runtime::new_with_cache(world, &cache).unwrap(); - assert_eq!( - runtime - .exec( - Command::Shell(script.into()), - Options::default(), - &mut output - ) - .await - .unwrap(), - 0 - ); - println!( - "state=fresh-warm command={script:?} seconds={:.6}", - start.elapsed().as_secs_f64() - ); - runtime.precompile().await.unwrap().unwrap(); - } - } - - #[test] - #[ignore = "Temporary T15 timing probe; remove before review"] - fn measure_optimizer() { - let bundle = Path::new(env!("CARGO_MANIFEST_DIR")).join("../target/userland/bundle/bin"); - let temp = tempfile::tempdir().unwrap(); - for opt in [wasmtime::OptLevel::Speed, wasmtime::OptLevel::None] { - let compiler = Compiler::with_opt(&temp.path().join(format!("{opt:?}")), opt).unwrap(); - for name in ["sh", "tr", "git", "rg"] { - let bytes = std::fs::read(bundle.join(format!("{name}.wasm"))).unwrap(); - let start = std::time::Instant::now(); - compiler.compile(&bytes).unwrap(); - println!( - "optimizer={opt:?} program={name} bytes={} compile_seconds={:.6}", - bytes.len(), - start.elapsed().as_secs_f64() - ); - } - } - } - #[tokio::test(flavor = "multi_thread")] async fn command_joins_precompile_even_after_waiter_cancellation() { let temp = tempfile::tempdir().unwrap(); diff --git a/host/src/process/mod.rs b/host/src/process/mod.rs index fd525e8..2db6095 100644 --- a/host/src/process/mod.rs +++ b/host/src/process/mod.rs @@ -167,13 +167,9 @@ impl Runtime { entries: Mutex::new(HashMap::new()), next: AtomicU32::new(1), }); - runtime.precompile(); + cache::precompile(runtime.compiler.clone(), runtime.world.root.clone()); Ok(runtime) } - /// Join startup precompilation of both command folders through the shared content slots. - pub fn precompile(&self) -> tokio::task::JoinHandle> { - cache::precompile(self.compiler.clone(), self.world.root.clone()) - } /// Number of distinct program contents compiled by this runtime. pub fn compiled_programs(&self) -> usize { self.cache.lock().unwrap().len()