Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/benches/thread_eager_init.rs
1685 views
1
use criterion::{Criterion, criterion_group, criterion_main};
2
use std::thread;
3
use std::time::{Duration, Instant};
4
use wasmtime::*;
5
6
fn measure_execution_time(c: &mut Criterion) {
7
// Baseline performance: a single measurement covers both initializing
8
// thread local resources and executing the first call.
9
//
10
// The other two bench functions should sum to this duration.
11
c.bench_function("lazy initialization at call", move |b| {
12
let (engine, module) = test_setup();
13
b.iter_custom(move |iters| {
14
(0..iters)
15
.map(|_| lazy_thread_instantiate(engine.clone(), module.clone()))
16
.sum()
17
})
18
});
19
20
// Using Engine::tls_eager_initialize: measure how long eager
21
// initialization takes on a new thread.
22
c.bench_function("eager initialization", move |b| {
23
let (engine, module) = test_setup();
24
b.iter_custom(move |iters| {
25
(0..iters)
26
.map(|_| {
27
let (init, _call) = eager_thread_instantiate(engine.clone(), module.clone());
28
init
29
})
30
.sum()
31
})
32
});
33
34
// Measure how long the first call takes on a thread after it has been
35
// eagerly initialized.
36
c.bench_function("call after eager initialization", move |b| {
37
let (engine, module) = test_setup();
38
b.iter_custom(move |iters| {
39
(0..iters)
40
.map(|_| {
41
let (_init, call) = eager_thread_instantiate(engine.clone(), module.clone());
42
call
43
})
44
.sum()
45
})
46
});
47
}
48
49
/// Creating a store and measuring the time to perform a call is the same behavior
50
/// in both setups.
51
fn duration_of_call(engine: &Engine, module: &Module) -> Duration {
52
let mut store = Store::new(engine, ());
53
let inst = Instance::new(&mut store, module, &[]).expect("instantiate");
54
let f = inst.get_func(&mut store, "f").expect("get f");
55
let f = f.typed::<(), ()>(&store).expect("type f");
56
57
let call = Instant::now();
58
f.call(&mut store, ()).expect("call f");
59
call.elapsed()
60
}
61
62
/// When wasmtime first runs a function on a thread, it needs to initialize
63
/// some thread-local resources and install signal handlers. This benchmark
64
/// spawns a new thread, and returns the duration it took to execute the first
65
/// function call made on that thread.
66
fn lazy_thread_instantiate(engine: Engine, module: Module) -> Duration {
67
thread::spawn(move || duration_of_call(&engine, &module))
68
.join()
69
.expect("thread joins")
70
}
71
/// This benchmark spawns a new thread, and records the duration to eagerly
72
/// initializes the thread local resources. It then creates a store and
73
/// instance, and records the duration it took to execute the first function
74
/// call.
75
fn eager_thread_instantiate(engine: Engine, module: Module) -> (Duration, Duration) {
76
thread::spawn(move || {
77
let init_start = Instant::now();
78
Engine::tls_eager_initialize();
79
let init_duration = init_start.elapsed();
80
81
(init_duration, duration_of_call(&engine, &module))
82
})
83
.join()
84
.expect("thread joins")
85
}
86
87
fn test_setup() -> (Engine, Module) {
88
// We only expect to create one Instance at a time, with a single memory.
89
let pool_count = 10;
90
91
let mut pool = PoolingAllocationConfig::default();
92
pool.total_memories(pool_count)
93
.total_stacks(pool_count)
94
.total_tables(pool_count);
95
let mut config = Config::new();
96
config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
97
let engine = Engine::new(&config).unwrap();
98
99
// The module has a memory (shouldn't matter) and a single function which is a no-op.
100
let module = Module::new(&engine, r#"(module (memory 1) (func (export "f")))"#).unwrap();
101
(engine, module)
102
}
103
104
criterion_group!(benches, measure_execution_time);
105
criterion_main!(benches);
106
107