Limit background compilation and reduce cold compile cost
This commit is contained in:
+5
-110
@@ -54,13 +54,10 @@ pub(super) struct Compiler {
|
||||
}
|
||||
impl Compiler {
|
||||
pub fn new(directory: &Path) -> anyhow::Result<Arc<Self>> {
|
||||
Self::with_opt(directory, wasmtime::OptLevel::Speed)
|
||||
}
|
||||
fn with_opt(directory: &Path, opt: wasmtime::OptLevel) -> anyhow::Result<Arc<Self>> {
|
||||
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<anyhow::Result<usize>> {
|
||||
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();
|
||||
|
||||
@@ -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<anyhow::Result<usize>> {
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user