From 035d541fd96cb411413df750f575c774ce8320a4 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 4 Jun 2021 14:04:14 -0700 Subject: [PATCH] add docs --- benches/thread_eager_init.rs | 95 +++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/benches/thread_eager_init.rs b/benches/thread_eager_init.rs index 9d35869cea..02f3c65f5c 100644 --- a/benches/thread_eager_init.rs +++ b/benches/thread_eager_init.rs @@ -4,8 +4,12 @@ use std::time::{Duration, Instant}; use wasmtime::*; fn measure_execution_time(c: &mut Criterion) { + // Baseline performance: a single measurment covers both initializing + // thread local resources and executing the first call. + // + // The other two bench functions should sum to this duration. c.bench_function("lazy initialization at call", move |b| { - let (engine, module) = test_engine(); + let (engine, module) = test_setup(); b.iter_custom(move |iters| { (0..iters) .into_iter() @@ -14,8 +18,10 @@ fn measure_execution_time(c: &mut Criterion) { }) }); + // Using Engine::tls_eager_initialize: measure how long eager + // initialization takes on a new thread. c.bench_function("eager initialization", move |b| { - let (engine, module) = test_engine(); + let (engine, module) = test_setup(); b.iter_custom(move |iters| { (0..iters) .into_iter() @@ -26,8 +32,11 @@ fn measure_execution_time(c: &mut Criterion) { .sum() }) }); + + // Measure how long the first call takes on a thread after it has been + // eagerly initialized. c.bench_function("call after eager initialization", move |b| { - let (engine, module) = test_engine(); + let (engine, module) = test_setup(); b.iter_custom(move |iters| { (0..iters) .into_iter() @@ -40,8 +49,47 @@ fn measure_execution_time(c: &mut Criterion) { }); } -fn test_engine() -> (Engine, Module) { - let pool_count = 1000; +/// Creating a store and measuring the time to perform a call is the same behavior +/// in both setups. +fn duration_of_call(engine: &Engine, module: &Module) -> Duration { + let mut store = Store::new(engine, ()); + let inst = Instance::new(&mut store, module, &[]).expect("instantiate"); + let f = inst.get_func(&mut store, "f").expect("get f"); + let f = f.typed::<(), (), _>(&store).expect("type f"); + + let call = Instant::now(); + f.call(&mut store, ()).expect("call f"); + call.elapsed() +} + +/// When wasmtime first runs a function on a thread, it needs to initialize +/// some thread-local resources and install signal handlers. This benchmark +/// spawns a new thread, and returns the duration it took to execute the first +/// function call made on that thread. +fn lazy_thread_instantiate(engine: Engine, module: Module) -> Duration { + thread::spawn(move || duration_of_call(&engine, &module)) + .join() + .expect("thread joins") +} +/// This benchmark spawns a new thread, and records the duration to eagerly +/// initializes the thread local resources. It then creates a store and +/// instance, and records the duration it took to execute the first function +/// call. +fn eager_thread_instantiate(engine: Engine, module: Module) -> (Duration, Duration) { + thread::spawn(move || { + let init_start = Instant::now(); + Engine::tls_eager_initialize().expect("eager init"); + let init_duration = init_start.elapsed(); + + (init_duration, duration_of_call(&engine, &module)) + }) + .join() + .expect("thread joins") +} + +fn test_setup() -> (Engine, Module) { + // We only expect to create one Instance at a time, with a single memory. + let pool_count = 10; let mut config = Config::new(); config.allocation_strategy(InstanceAllocationStrategy::Pooling { @@ -55,45 +103,12 @@ fn test_engine() -> (Engine, Module) { memory_reservation_size: 1, }, }); - let engine = Engine::new(&config).unwrap(); + + // The module has a memory (shouldn't matter) and a single function which is a no-op. let module = Module::new(&engine, r#"(module (memory 1) (func (export "f")))"#).unwrap(); (engine, module) } -fn lazy_thread_instantiate(engine: Engine, module: Module) -> Duration { - thread::spawn(move || { - let mut store = Store::new(&engine, ()); - let inst = Instance::new(&mut store, &module, &[]).expect("instantiate"); - let f = inst.get_func(&mut store, "f").expect("get f"); - let f = f.typed::<(), (), _>(&store).expect("type f"); - - let call = Instant::now(); - f.call(&mut store, ()).expect("call f"); - call.elapsed() - }) - .join() - .expect("thread joins") -} - -fn eager_thread_instantiate(engine: Engine, module: Module) -> (Duration, Duration) { - thread::spawn(move || { - let init_start = Instant::now(); - Engine::tls_eager_initialize().expect("eager init"); - let init_duration = init_start.elapsed(); - - let mut store = Store::new(&engine, ()); - let inst = Instance::new(&mut store, &module, &[]).expect("instantiate"); - let f = inst.get_func(&mut store, "f").expect("get f"); - let f = f.typed::<(), (), _>(&store).expect("type f"); - - let call = Instant::now(); - f.call(&mut store, ()).expect("call f"); - (init_duration, call.elapsed()) - }) - .join() - .expect("thread joins") -} - criterion_group!(benches, measure_execution_time); criterion_main!(benches);