xref: /wasmtime-44.0.1/benches/trap.rs (revision 94740588)
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