Skip to content

Commit 38e3750

Browse files
committed
update benchmarks
1 parent 74df45e commit 38e3750

File tree

2 files changed

+58
-21
lines changed

2 files changed

+58
-21
lines changed

README.md

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ Based on the benchmark results, pattern matching in Rust is significantly faster
88

99
```
1010
cargo bench
11-
Compiling rust-dynamic-dispatch-performance v0.1.0 (/Users/tushar/Documents/Projects/temp/rust-dynamic-dispatch-performance)
12-
Finished `bench` profile [optimized] target(s) in 1.66s
11+
Compiling rust-dynamic-dispatch-performance v0.1.0 (/Users/tushar/Documents/Projects/rust-dynamic-dispatch-performance)
12+
Finished `bench` profile [optimized] target(s) in 1.20s
1313
Running unittests src/main.rs (target/release/deps/rust_dynamic_dispatch_performance-5c61e2e8a62037f3)
1414
1515
running 0 tests
@@ -18,21 +18,63 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
1818
1919
Running benches/benchmarks.rs (target/release/deps/benchmarks-738a32214e4b14ea)
2020
Dispatch vs Matching/Dynamic Dispatch
21-
time: [22.946 ns 25.470 ns 28.807 ns]
22-
change: [+3.7542% +10.309% +18.945%] (p = 0.00 < 0.05)
23-
Performance has regressed.
24-
Found 16 outliers among 100 measurements (16.00%)
21+
time: [2.5226 ns 2.5325 ns 2.5420 ns]
22+
change: [+0.3857% +0.8665% +1.3628%] (p = 0.00 < 0.05)
23+
Change within noise threshold.
24+
Found 5 outliers among 100 measurements (5.00%)
2525
4 (4.00%) high mild
26-
12 (12.00%) high severe
26+
1 (1.00%) high severe
2727
Dispatch vs Matching/Pattern Matching
28-
time: [317.89 ps 320.03 ps 322.71 ps]
29-
change: [-5.1626% -2.1683% +0.0642%] (p = 0.12 > 0.05)
30-
No change in performance detected.
31-
Found 9 outliers among 100 measurements (9.00%)
32-
3 (3.00%) high mild
28+
time: [375.26 ps 377.67 ps 380.79 ps]
29+
change: [-76.135% -75.895% -75.626%] (p = 0.00 < 0.05)
30+
Performance has improved.
31+
Found 10 outliers among 100 measurements (10.00%)
32+
4 (4.00%) high mild
3333
6 (6.00%) high severe
3434
```
3535

36+
## Update 1
37+
38+
We were curious to see why pattern matching was significantly faster compared to dynamic dispatch, so we posted benchmarks on [Reddit](https://www.reddit.com/r/rust/comments/1cx7qvi/performance_pattern_matching_vs_dynamic_dispatch/). We received interesting feedback about the benchmark setup, such as the unnecessary heap allocation for dynamic dispatch. After addressing this, the performance difference was marginal.
39+
40+
Digging deeper, especially after this [GitHub comment](https://github.com/tailcallhq/rust-benchmarks/issues/2) and further analysis on [Rust Compiler Explorer](https://rust.godbolt.org/), we discovered that the compiler optimizes the generated machine code based on the seed value set in the variable `output`. When set to `0`, the additions and multiplications would always return `0` regardless of the contents of the passed array. This optimization resulted in the pattern matching case reducing to (an optimization that was unavailable to the `dyn` implementation):
41+
42+
```asm
43+
xor eax, eax
44+
ret
45+
```
46+
47+
After correcting this by setting the seed value to `1`, the benchmarks showed a significant improvement, making pattern matching only **2.7x faster** than dynamic dispatch.
48+
49+
```
50+
cargo bench
51+
Compiling rust-dynamic-dispatch-performance v0.1.0 (/Users/tushar/Documents/Projects/rust-dynamic-dispatch-performance)
52+
Finished `bench` profile [optimized] target(s) in 1.36s
53+
Running unittests src/main.rs (target/release/deps/rust_dynamic_dispatch_performance-5c61e2e8a62037f3)
54+
55+
running 0 tests
56+
57+
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
58+
59+
Running benches/benchmarks.rs (target/release/deps/benchmarks-738a32214e4b14ea)
60+
Benchmarking Dispatch vs Matching/Dynamic Dispatch: Collecting 100 samples in
61+
Dispatch vs Matching/Dynamic Dispatch
62+
time: [2.5552 ns 2.5824 ns 2.6199 ns]
63+
change: [+2.0878% +8.3408% +15.931%] (p = 0.01 < 0.05)
64+
Performance has regressed.
65+
Found 20 outliers among 100 measurements (20.00%)
66+
1 (1.00%) high mild
67+
19 (19.00%) high severe
68+
Benchmarking Dispatch vs Matching/Pattern Matching: Collecting 100 samples in
69+
Dispatch vs Matching/Pattern Matching
70+
time: [947.52 ps 949.81 ps 952.50 ps]
71+
change: [+147.37% +149.83% +151.38%] (p = 0.00 < 0.05)
72+
Performance has regressed.
73+
Found 2 outliers among 100 measurements (2.00%)
74+
1 (1.00%) high mild
75+
1 (1.00%) high severe
76+
```
77+
3678
Happy coding! 🦀
3779

3880
PS: Feel free to raise a PR if you think there is something wrong with the way the benchmarks are designed.

benches/benchmarks.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use criterion::{black_box, criterion_group, criterion_main, Criterion};
2-
use std::time::Duration;
32

43
trait DoSomething {
54
fn do_it(&self, i: i32) -> i32;
@@ -27,16 +26,14 @@ enum Action {
2726
Two(ActionTwo),
2827
}
2928

30-
fn dynamic_dispatch(actions: &Vec<Box<dyn DoSomething>>) -> i32 {
31-
let mut output = 0;
29+
fn dynamic_dispatch(actions: &Vec<Box<dyn DoSomething>>, mut output: i32) -> i32 {
3230
for action in actions {
3331
output = output + action.do_it(output);
3432
}
3533
output
3634
}
3735

38-
fn pattern_matching(actions: &Vec<Action>) -> i32 {
39-
let mut output = 0;
36+
fn pattern_matching(actions: &Vec<Action>, mut output: i32) -> i32 {
4037
for action in actions {
4138
match action {
4239
Action::One(a) => output = output + a.do_it(output),
@@ -52,18 +49,16 @@ fn benchmark(c: &mut Criterion) {
5249
let dynamic_actions: Vec<Box<dyn DoSomething>> = vec![Box::new(ActionOne), Box::new(ActionTwo)];
5350
let static_actions: Vec<Action> = vec![Action::One(ActionOne), Action::Two(ActionTwo)];
5451

55-
group.measurement_time(Duration::new(10, 0));
56-
5752
group.bench_function("Dynamic Dispatch", |b| {
5853
b.iter(|| {
59-
let output = dynamic_dispatch(&dynamic_actions);
54+
let output = dynamic_dispatch(&dynamic_actions, 1);
6055
black_box(output)
6156
})
6257
});
6358

6459
group.bench_function("Pattern Matching", |b| {
6560
b.iter(|| {
66-
let output = pattern_matching(&static_actions);
61+
let output = pattern_matching(&static_actions, 1);
6762
black_box(output)
6863
})
6964
});

0 commit comments

Comments
 (0)