Browse Source

add docs

pull/2946/head
Pat Hickey 3 years ago
parent
commit
035d541fd9
  1. 91
      benches/thread_eager_init.rs

91
benches/thread_eager_init.rs

@ -4,8 +4,12 @@ use std::time::{Duration, Instant};
use wasmtime::*; use wasmtime::*;
fn measure_execution_time(c: &mut Criterion) { 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| { c.bench_function("lazy initialization at call", move |b| {
let (engine, module) = test_engine(); let (engine, module) = test_setup();
b.iter_custom(move |iters| { b.iter_custom(move |iters| {
(0..iters) (0..iters)
.into_iter() .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| { c.bench_function("eager initialization", move |b| {
let (engine, module) = test_engine(); let (engine, module) = test_setup();
b.iter_custom(move |iters| { b.iter_custom(move |iters| {
(0..iters) (0..iters)
.into_iter() .into_iter()
@ -26,8 +32,11 @@ fn measure_execution_time(c: &mut Criterion) {
.sum() .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| { c.bench_function("call after eager initialization", move |b| {
let (engine, module) = test_engine(); let (engine, module) = test_setup();
b.iter_custom(move |iters| { b.iter_custom(move |iters| {
(0..iters) (0..iters)
.into_iter() .into_iter()
@ -40,60 +49,66 @@ fn measure_execution_time(c: &mut Criterion) {
}); });
} }
fn test_engine() -> (Engine, Module) { /// Creating a store and measuring the time to perform a call is the same behavior
let pool_count = 1000; /// in both setups.
fn duration_of_call(engine: &Engine, module: &Module) -> Duration {
let mut config = Config::new(); let mut store = Store::new(engine, ());
config.allocation_strategy(InstanceAllocationStrategy::Pooling { let inst = Instance::new(&mut store, module, &[]).expect("instantiate");
strategy: PoolingAllocationStrategy::NextAvailable,
module_limits: ModuleLimits {
memory_pages: 1,
..Default::default()
},
instance_limits: InstanceLimits {
count: pool_count,
memory_reservation_size: 1,
},
});
let engine = Engine::new(&config).unwrap();
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 = inst.get_func(&mut store, "f").expect("get f");
let f = f.typed::<(), (), _>(&store).expect("type f"); let f = f.typed::<(), (), _>(&store).expect("type f");
let call = Instant::now(); let call = Instant::now();
f.call(&mut store, ()).expect("call f"); f.call(&mut store, ()).expect("call f");
call.elapsed() 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() .join()
.expect("thread joins") .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) { fn eager_thread_instantiate(engine: Engine, module: Module) -> (Duration, Duration) {
thread::spawn(move || { thread::spawn(move || {
let init_start = Instant::now(); let init_start = Instant::now();
Engine::tls_eager_initialize().expect("eager init"); Engine::tls_eager_initialize().expect("eager init");
let init_duration = init_start.elapsed(); let init_duration = init_start.elapsed();
let mut store = Store::new(&engine, ()); (init_duration, duration_of_call(&engine, &module))
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() .join()
.expect("thread joins") .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 {
strategy: PoolingAllocationStrategy::NextAvailable,
module_limits: ModuleLimits {
memory_pages: 1,
..Default::default()
},
instance_limits: InstanceLimits {
count: pool_count,
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)
}
criterion_group!(benches, measure_execution_time); criterion_group!(benches, measure_execution_time);
criterion_main!(benches); criterion_main!(benches);

Loading…
Cancel
Save