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