1 use criterion::*;
2 use wasmtime::*;
3
4 criterion_main!(benches);
5 criterion_group!(benches, bench_traps);
6
bench_traps(c: &mut Criterion)7 fn bench_traps(c: &mut Criterion) {
8 bench_multi_threaded_traps(c);
9 bench_many_modules_registered_traps(c);
10 bench_many_stack_frames_traps(c);
11 bench_host_wasm_frames_traps(c);
12 }
13
bench_multi_threaded_traps(c: &mut Criterion)14 fn bench_multi_threaded_traps(c: &mut Criterion) {
15 let mut group = c.benchmark_group("multi-threaded-traps");
16
17 for num_bg_threads in vec![0, 1, 2, 4, 8, 16] {
18 group.throughput(Throughput::Elements(num_bg_threads));
19 group.bench_with_input(
20 BenchmarkId::from_parameter(num_bg_threads),
21 &num_bg_threads,
22 |b, &num_bg_threads| {
23 let engine = Engine::default();
24 let module = module(&engine, 10).unwrap();
25
26 b.iter_custom(|iters| {
27 let (started_sender, started_receiver) = std::sync::mpsc::channel();
28
29 // Spawn threads in the background doing infinite work.
30 let threads = (0..num_bg_threads)
31 .map(|_| {
32 let (done_sender, done_receiver) = std::sync::mpsc::channel();
33 let handle = std::thread::spawn({
34 let engine = engine.clone();
35 let module = module.clone();
36 let started_sender = started_sender.clone();
37 move || {
38 let mut store = Store::new(&engine, ());
39 let instance = Instance::new(&mut store, &module, &[]).unwrap();
40 let f =
41 instance.get_typed_func::<(), ()>(&mut store, "").unwrap();
42
43 // Notify the parent thread that we are
44 // doing background work now.
45 started_sender.send(()).unwrap();
46
47 // Keep doing background work until the
48 // parent tells us to stop.
49 loop {
50 if let Ok(()) = done_receiver.try_recv() {
51 return;
52 }
53 assert!(f.call(&mut store, ()).is_err());
54 }
55 }
56 });
57 (handle, done_sender)
58 })
59 .collect::<Vec<_>>();
60
61 // Wait on all the threads to start up.
62 for _ in 0..num_bg_threads {
63 let _ = started_receiver.recv().unwrap();
64 }
65
66 let mut store = Store::new(&engine, ());
67 let instance = Instance::new(&mut store, &module, &[]).unwrap();
68 let f = instance.get_typed_func::<(), ()>(&mut store, "").unwrap();
69
70 // Measure how long it takes to do `iters` worth of traps
71 // while there is a bunch of background work going on.
72 let start = std::time::Instant::now();
73 for _ in 0..iters {
74 assert!(f.call(&mut store, ()).is_err());
75 }
76 let elapsed = start.elapsed();
77
78 // Clean up all of our background threads.
79 threads.into_iter().for_each(|(handle, done_sender)| {
80 done_sender.send(()).unwrap();
81 handle.join().unwrap();
82 });
83
84 elapsed
85 });
86 },
87 );
88 }
89
90 group.finish();
91 }
92
bench_many_modules_registered_traps(c: &mut Criterion)93 fn bench_many_modules_registered_traps(c: &mut Criterion) {
94 let mut group = c.benchmark_group("many-modules-registered-traps");
95
96 for num_modules in vec![1, 8, 64, 512, 4096] {
97 group.throughput(Throughput::Elements(num_modules));
98 group.bench_with_input(
99 BenchmarkId::from_parameter(num_modules),
100 &num_modules,
101 |b, &num_modules| {
102 let engine = Engine::default();
103 let modules = (0..num_modules)
104 .map(|_| module(&engine, 10).unwrap())
105 .collect::<Vec<_>>();
106
107 b.iter_custom(|iters| {
108 let mut store = Store::new(&engine, ());
109 let instance = Instance::new(&mut store, modules.last().unwrap(), &[]).unwrap();
110 let f = instance.get_typed_func::<(), ()>(&mut store, "").unwrap();
111
112 let start = std::time::Instant::now();
113 for _ in 0..iters {
114 assert!(f.call(&mut store, ()).is_err());
115 }
116 start.elapsed()
117 });
118 },
119 );
120 }
121
122 group.finish()
123 }
124
bench_many_stack_frames_traps(c: &mut Criterion)125 fn bench_many_stack_frames_traps(c: &mut Criterion) {
126 let mut group = c.benchmark_group("many-stack-frames-traps");
127
128 for num_stack_frames in vec![1, 8, 64, 512] {
129 group.throughput(Throughput::Elements(num_stack_frames));
130 group.bench_with_input(
131 BenchmarkId::from_parameter(num_stack_frames),
132 &num_stack_frames,
133 |b, &num_stack_frames| {
134 let engine = Engine::default();
135 let module = module(&engine, num_stack_frames).unwrap();
136
137 b.iter_custom(|iters| {
138 let mut store = Store::new(&engine, ());
139 let instance = Instance::new(&mut store, &module, &[]).unwrap();
140 let f = instance.get_typed_func::<(), ()>(&mut store, "").unwrap();
141
142 let start = std::time::Instant::now();
143 for _ in 0..iters {
144 assert!(f.call(&mut store, ()).is_err());
145 }
146 start.elapsed()
147 });
148 },
149 );
150 }
151
152 group.finish()
153 }
154
bench_host_wasm_frames_traps(c: &mut Criterion)155 fn bench_host_wasm_frames_traps(c: &mut Criterion) {
156 let mut group = c.benchmark_group("host-wasm-frames-traps");
157
158 let wat = r#"
159 (module
160 (import "" "" (func $host_func (param i32)))
161 (func (export "f") (param i32)
162 local.get 0
163 i32.eqz
164 if
165 unreachable
166 end
167
168 local.get 0
169 i32.const 1
170 i32.sub
171 call $host_func
172 )
173 )
174 "#;
175
176 let engine = Engine::default();
177 let module = Module::new(&engine, wat).unwrap();
178
179 for num_stack_frames in vec![20, 40, 60, 80, 100, 120, 140, 160, 180, 200] {
180 group.throughput(Throughput::Elements(num_stack_frames));
181 group.bench_with_input(
182 BenchmarkId::from_parameter(num_stack_frames),
183 &num_stack_frames,
184 |b, &num_stack_frames| {
185 b.iter_custom(|iters| {
186 let mut store = Store::new(&engine, ());
187 let host_func = Func::new(
188 &mut store,
189 FuncType::new(&engine, vec![ValType::I32], vec![]),
190 |mut caller, args, _results| {
191 let f = caller.get_export("f").unwrap();
192 let f = f.into_func().unwrap();
193 f.call(caller, args, &mut [])?;
194 Ok(())
195 },
196 );
197 let instance = Instance::new(&mut store, &module, &[host_func.into()]).unwrap();
198 let f = instance
199 .get_typed_func::<(i32,), ()>(&mut store, "f")
200 .unwrap();
201
202 let start = std::time::Instant::now();
203 for _ in 0..iters {
204 assert!(f.call(&mut store, (num_stack_frames as i32,)).is_err());
205 }
206 start.elapsed()
207 });
208 },
209 );
210 }
211
212 group.finish()
213 }
214
module(engine: &Engine, num_funcs: u64) -> Result<Module>215 fn module(engine: &Engine, num_funcs: u64) -> Result<Module> {
216 let mut wat = String::new();
217 wat.push_str("(module\n");
218 for i in 0..num_funcs {
219 let j = i + 1;
220 wat.push_str(&format!("(func $f{i} call $f{j})\n"));
221 }
222 wat.push_str(&format!("(func $f{num_funcs} unreachable)\n"));
223 wat.push_str(&format!("(export \"\" (func $f0))\n"));
224 wat.push_str(")\n");
225
226 Module::new(engine, &wat)
227 }
228