Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/benches/instantiation.rs
1685 views
1
use anyhow::Result;
2
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
3
use std::cell::LazyCell;
4
use std::path::Path;
5
use std::process::Command;
6
use std::sync::Arc;
7
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
8
use std::thread;
9
use wasmtime::*;
10
use wasmtime_wasi::{WasiCtx, p1::WasiP1Ctx};
11
12
fn store(engine: &Engine) -> Store<WasiP1Ctx> {
13
let wasi = WasiCtx::builder().build_p1();
14
Store::new(engine, wasi)
15
}
16
17
fn instantiate(pre: &InstancePre<WasiP1Ctx>, engine: &Engine) -> Result<()> {
18
let mut store = store(engine);
19
let _instance = pre.instantiate(&mut store)?;
20
Ok(())
21
}
22
23
fn benchmark_name(strategy: &InstanceAllocationStrategy) -> &'static str {
24
match strategy {
25
InstanceAllocationStrategy::OnDemand => "default",
26
InstanceAllocationStrategy::Pooling { .. } => "pooling",
27
_ => unreachable!(),
28
}
29
}
30
31
fn bench_sequential(c: &mut Criterion, path: &Path) {
32
let mut group = c.benchmark_group("sequential");
33
34
for strategy in strategies() {
35
let id = BenchmarkId::new(
36
benchmark_name(&strategy),
37
path.file_name().unwrap().to_str().unwrap(),
38
);
39
let state = LazyCell::new(|| {
40
let mut config = Config::default();
41
config.allocation_strategy(strategy.clone());
42
43
let engine = Engine::new(&config).expect("failed to create engine");
44
let module = Module::from_file(&engine, path).unwrap_or_else(|e| {
45
panic!("failed to load benchmark `{}`: {:?}", path.display(), e)
46
});
47
let mut linker = Linker::new(&engine);
48
// Add these imports so we can benchmark instantiation of Sightglass
49
// benchmark programs.
50
linker.func_wrap("bench", "start", || {}).unwrap();
51
linker.func_wrap("bench", "end", || {}).unwrap();
52
wasmtime_wasi::p1::add_to_linker_sync(&mut linker, |cx| cx).unwrap();
53
let pre = linker
54
.instantiate_pre(&module)
55
.expect("failed to pre-instantiate");
56
(engine, pre)
57
});
58
59
group.bench_function(id, |b| {
60
let (engine, pre) = &*state;
61
b.iter(|| {
62
instantiate(&pre, &engine).expect("failed to instantiate module");
63
});
64
});
65
}
66
67
group.finish();
68
}
69
70
fn bench_parallel(c: &mut Criterion, path: &Path) {
71
let mut group = c.benchmark_group("parallel");
72
73
for strategy in strategies() {
74
let state = LazyCell::new(|| {
75
let mut config = Config::default();
76
config.allocation_strategy(strategy.clone());
77
78
let engine = Engine::new(&config).expect("failed to create engine");
79
let module =
80
Module::from_file(&engine, path).expect("failed to load WASI example module");
81
let mut linker = Linker::new(&engine);
82
// Add these imports so we can benchmark instantiation of Sightglass
83
// benchmark programs.
84
linker.func_wrap("bench", "start", || {}).unwrap();
85
linker.func_wrap("bench", "end", || {}).unwrap();
86
wasmtime_wasi::p1::add_to_linker_sync(&mut linker, |cx| cx).unwrap();
87
let pre = Arc::new(
88
linker
89
.instantiate_pre(&module)
90
.expect("failed to pre-instantiate"),
91
);
92
(engine, pre)
93
});
94
95
for threads in 1..=num_cpus::get_physical().min(16) {
96
let name = format!(
97
"{}: with {} thread{}",
98
path.file_name().unwrap().to_str().unwrap(),
99
threads,
100
if threads == 1 { "" } else { "s" }
101
);
102
let id = BenchmarkId::new(benchmark_name(&strategy), name);
103
group.bench_function(id, |b| {
104
let (engine, pre) = &*state;
105
// Spin up N-1 threads doing background instantiations to
106
// simulate concurrent instantiations.
107
let done = Arc::new(AtomicBool::new(false));
108
let count = Arc::new(AtomicUsize::new(0));
109
let workers = (0..threads - 1)
110
.map(|_| {
111
let pre = pre.clone();
112
let done = done.clone();
113
let engine = engine.clone();
114
let count = count.clone();
115
thread::spawn(move || {
116
count.fetch_add(1, SeqCst);
117
while !done.load(SeqCst) {
118
instantiate(&pre, &engine).unwrap();
119
}
120
})
121
})
122
.collect::<Vec<_>>();
123
124
// Wait for our workers to all get started and have
125
// instantiated their first module, at which point they'll
126
// all be spinning.
127
while count.load(SeqCst) != threads - 1 {
128
thread::yield_now();
129
}
130
131
// Now that our background work is configured we can
132
// benchmark the amount of time it takes to instantiate this
133
// module.
134
b.iter(|| {
135
instantiate(&pre, &engine).expect("failed to instantiate module");
136
});
137
138
// Shut down this benchmark iteration by signalling to
139
// worker threads they should exit and then wait for them to
140
// have reached the exit point.
141
done.store(true, SeqCst);
142
for t in workers {
143
t.join().unwrap();
144
}
145
});
146
}
147
}
148
149
group.finish();
150
}
151
152
fn bench_deserialize_module(c: &mut Criterion, path: &Path) {
153
let mut group = c.benchmark_group("deserialize");
154
155
let name = path.file_name().unwrap().to_str().unwrap();
156
let tmpfile = tempfile::NamedTempFile::new().unwrap();
157
let state = LazyCell::new(|| {
158
let engine = Engine::default();
159
let module = Module::from_file(&engine, path).expect("failed to load WASI example module");
160
let bytes = module.serialize().unwrap();
161
std::fs::write(tmpfile.path(), bytes.clone()).unwrap();
162
(engine, bytes, tmpfile.path())
163
});
164
group.bench_function(BenchmarkId::new("deserialize", name), |b| {
165
let (engine, bytes, _) = &*state;
166
b.iter(|| unsafe {
167
Module::deserialize(&engine, bytes).unwrap();
168
});
169
});
170
group.bench_function(BenchmarkId::new("deserialize_file", name), |b| {
171
let (engine, _, path) = &*state;
172
b.iter(|| unsafe {
173
Module::deserialize_file(&engine, path).unwrap();
174
});
175
});
176
177
group.finish();
178
}
179
180
fn build_wasi_example() {
181
println!("Building WASI example module...");
182
if !Command::new("cargo")
183
.args(&[
184
"build",
185
"--release",
186
"-p",
187
"example-wasi-wasm",
188
"--target",
189
"wasm32-wasip1",
190
])
191
.spawn()
192
.expect("failed to run cargo to build WASI example")
193
.wait()
194
.expect("failed to wait for cargo to build")
195
.success()
196
{
197
panic!("failed to build WASI example for target `wasm32-wasip1`");
198
}
199
200
std::fs::copy(
201
"target/wasm32-wasip1/release/wasi.wasm",
202
"benches/instantiation/wasi.wasm",
203
)
204
.expect("failed to copy WASI example module");
205
}
206
207
fn bench_instantiation(c: &mut Criterion) {
208
build_wasi_example();
209
210
for file in std::fs::read_dir("benches/instantiation").unwrap() {
211
let path = file.unwrap().path();
212
bench_sequential(c, &path);
213
bench_parallel(c, &path);
214
bench_deserialize_module(c, &path);
215
}
216
}
217
218
fn strategies() -> impl Iterator<Item = InstanceAllocationStrategy> {
219
[
220
InstanceAllocationStrategy::OnDemand,
221
InstanceAllocationStrategy::Pooling({
222
let mut config = PoolingAllocationConfig::default();
223
config.max_memory_size(10_000 << 16);
224
config
225
}),
226
]
227
.into_iter()
228
}
229
230
criterion_group!(benches, bench_instantiation);
231
criterion_main!(benches);
232
233