Skip to content

Commit 9b3c880

Browse files
committed
update benchmark results
1 parent 9736ae9 commit 9b3c880

File tree

3 files changed

+96
-65
lines changed

3 files changed

+96
-65
lines changed

Cargo.toml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,16 @@ readme = "README.md"
1414

1515
[dev-dependencies]
1616
# Benchmarks
17-
criterion = "0.3.4"
18-
actix-router = "0.2.7"
19-
regex = "1.5.4"
20-
route-recognizer = "0.3.0"
17+
criterion = "0.5"
18+
actix-router = "0.5"
19+
regex = "1"
20+
route-recognizer = "0.3"
2121
gonzales = "0.0.3-beta"
22-
path-tree = "0.2.2"
23-
routefinder = "0.5.2"
22+
path-tree = "0.8"
23+
routefinder = "0.5"
24+
wayfind = "0.7"
2425

25-
# examples
26+
# Examples
2627
tower = { version = "0.4", features = ["make", "util"] }
2728
tokio = { version = "1", features = ["full"] }
2829
http-body-util = "0.1"

README.md

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
5959
assert!(m.at("/").is_err());
6060
```
6161

62-
The literal characters `{` and `}` may be included in a static route by escaping them with the same character. For example, the `{` character is escaped with `{{` and the `}` character is escaped with `}}`.
62+
The literal characters `{` and `}` may be included in a static route by escaping them with the same character.
63+
For example, the `{` character is escaped with `{{` and the `}` character is escaped with `}}`.
6364

6465
```rust,ignore
6566
let mut m = Router::new();
@@ -86,7 +87,8 @@ m.insert("/{*filepath}", "...").unwrap(); // Priority: 2
8687

8788
## How does it work?
8889

89-
The router takes advantage of the fact that URL routes generally follow a hierarchical structure. Routes are stored them in a radix trie that makes heavy use of common prefixes.
90+
The router takes advantage of the fact that URL routes generally follow a hierarchical structure.
91+
Routes are stored them in a radix trie that makes heavy use of common prefixes.
9092

9193
```text
9294
Priority Path Value
@@ -102,34 +104,40 @@ Priority Path Value
102104
1 └contact\ 8
103105
```
104106

105-
This allows us to reduce the route search to a small number of branches. Child nodes on the same level of the tree are also prioritized
106-
by the number of children with registered values, increasing the chance of choosing the correct branch of the first try.
107+
This allows us to reduce the route search to a small number of branches. Child nodes on the same level of the tree are also
108+
prioritized by the number of children with registered values, increasing the chance of choosing the correct branch of the first try.
107109

108110
## Benchmarks
109111

110-
As it turns out, this method of routing is extremely fast. In a benchmark matching 4 paths against 130 registered routes, `matchit` find the correct routes
111-
in under 200 nanoseconds, an order of magnitude faster than most other routers. You can view the benchmark code [here](https://github.com/ibraheemdev/matchit/blob/master/benches/bench.rs).
112+
As it turns out, this method of routing is extremely fast. Below are the benchmark results matching against 130 registered routes.
113+
You can view the benchmark code [here](https://github.com/ibraheemdev/matchit/blob/master/benches/bench.rs).
112114

113115
```text
114116
Compare Routers/matchit
115-
time: [175.96 ns 176.39 ns 176.84 ns]
117+
time: [2.4451 µs 2.4456 µs 2.4462 µs]
116118
117-
Compare Routers/actix
118-
time: [26.805 us 26.811 us 26.816 us]
119+
Compare Routers/gonzales
120+
time: [4.2618 µs 4.2632 µs 4.2646 µs]
119121
120122
Compare Routers/path-tree
121-
time: [468.95 ns 470.34 ns 471.65 ns]
123+
time: [4.8666 µs 4.8696 µs 4.8728 µs]
122124
123-
Compare Routers/regex
124-
time: [22.539 us 22.584 us 22.639 us]
125+
Compare Routers/wayfind
126+
time: [4.9440 µs 4.9539 µs 4.9668 µs]
125127
126128
Compare Routers/route-recognizer
127-
time: [3.7552 us 3.7732 us 3.8027 us]
129+
time: [49.203 µs 49.214 µs 49.226 µs]
128130
129131
Compare Routers/routefinder
130-
time: [5.7313 us 5.7405 us 5.7514 us]
132+
time: [70.598 µs 70.636 µs 70.670 µs]
133+
134+
Compare Routers/actix
135+
time: [453.91 µs 454.01 µs 454.11 µs]
136+
137+
Compare Routers/regex
138+
time: [421.76 µs 421.82 µs 421.89 µs]
131139
```
132140

133141
## Credits
134142

135-
A lot of the code in this package was based on Julien Schmidt's [`httprouter`](https://github.com/julienschmidt/httprouter).
143+
A lot of the code in this package was inspired by Julien Schmidt's [`httprouter`](https://github.com/julienschmidt/httprouter).

benches/bench.rs

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,112 @@
11
use criterion::{black_box, criterion_group, criterion_main, Criterion};
22

3-
fn call() -> impl IntoIterator<Item = &'static str> {
4-
[
5-
"/user/repos",
6-
"/repos/rust-lang/rust/stargazers",
7-
"/orgs/rust-lang/public_members/nikomatsakis",
8-
"/repos/rust-lang/rust/releases/1.51.0",
9-
]
10-
}
11-
123
fn compare_routers(c: &mut Criterion) {
134
let mut group = c.benchmark_group("Compare Routers");
145

6+
let paths = routes!(literal).to_vec();
7+
158
let mut matchit = matchit::Router::new();
16-
for route in register!(brackets) {
9+
for route in routes!(brackets) {
1710
matchit.insert(route, true).unwrap();
1811
}
1912
group.bench_function("matchit", |b| {
2013
b.iter(|| {
21-
for route in black_box(call()) {
22-
black_box(matchit.at(route).unwrap());
14+
for path in black_box(&paths) {
15+
let result = black_box(matchit.at(path).unwrap());
16+
assert!(*result.value);
17+
}
18+
});
19+
});
20+
21+
let mut wayfind = wayfind::Router::new();
22+
for route in routes!(brackets) {
23+
wayfind.insert(route, true).unwrap();
24+
}
25+
let wayfind_paths = paths
26+
.iter()
27+
.copied()
28+
.flat_map(wayfind::Path::new)
29+
.collect::<Vec<_>>();
30+
group.bench_function("wayfind", |b| {
31+
b.iter(|| {
32+
for path in black_box(&wayfind_paths) {
33+
let result = black_box(wayfind.search(&path).unwrap().unwrap());
34+
assert!(*result.data);
2335
}
2436
});
2537
});
2638

2739
let mut path_tree = path_tree::PathTree::new();
28-
for route in register!(colon) {
29-
path_tree.insert(route, true);
40+
for route in routes!(colon) {
41+
let _ = path_tree.insert(route, true);
3042
}
3143
group.bench_function("path-tree", |b| {
3244
b.iter(|| {
33-
for route in black_box(call()) {
34-
black_box(path_tree.find(route).unwrap());
45+
for path in black_box(&paths) {
46+
let result = black_box(path_tree.find(path).unwrap());
47+
assert!(*result.0);
3548
}
3649
});
3750
});
3851

39-
let gonzales = gonzales::RouterBuilder::new().build(register!(brackets));
52+
let registered = routes!(brackets);
53+
let gonzales = gonzales::RouterBuilder::new().build(registered);
4054
group.bench_function("gonzales", |b| {
4155
b.iter(|| {
42-
for route in black_box(call()) {
43-
black_box(gonzales.route(route).unwrap());
56+
for path in black_box(&paths) {
57+
let result = black_box(gonzales.route(path).unwrap());
58+
assert!(registered.get(result.get_index()).is_some());
4459
}
4560
});
4661
});
4762

4863
let mut actix = actix_router::Router::<bool>::build();
49-
for route in register!(brackets) {
64+
for route in routes!(brackets) {
5065
actix.path(route, true);
5166
}
5267
let actix = actix.finish();
5368
group.bench_function("actix", |b| {
5469
b.iter(|| {
55-
for route in black_box(call()) {
56-
let mut path = actix_router::Path::new(route);
57-
black_box(actix.recognize(&mut path).unwrap());
70+
for path in black_box(&paths) {
71+
let mut path = actix_router::Path::new(*path);
72+
let result = black_box(actix.recognize(&mut path).unwrap());
73+
assert!(*result.0);
5874
}
5975
});
6076
});
6177

62-
let regex_set = regex::RegexSet::new(register!(regex)).unwrap();
78+
let regex_set = regex::RegexSet::new(routes!(regex)).unwrap();
6379
group.bench_function("regex", |b| {
6480
b.iter(|| {
65-
for route in black_box(call()) {
66-
black_box(regex_set.matches(route));
81+
for path in black_box(&paths) {
82+
let result = black_box(regex_set.matches(path));
83+
assert!(result.matched_any());
6784
}
6885
});
6986
});
7087

7188
let mut route_recognizer = route_recognizer::Router::new();
72-
for route in register!(colon) {
89+
for route in routes!(colon) {
7390
route_recognizer.add(route, true);
7491
}
7592
group.bench_function("route-recognizer", |b| {
7693
b.iter(|| {
77-
for route in black_box(call()) {
78-
black_box(route_recognizer.recognize(route).unwrap());
94+
for path in black_box(&paths) {
95+
let result = black_box(route_recognizer.recognize(path).unwrap());
96+
assert!(**result.handler());
7997
}
8098
});
8199
});
82100

83101
let mut routefinder = routefinder::Router::new();
84-
for route in register!(colon) {
102+
for route in routes!(colon) {
85103
routefinder.add(route, true).unwrap();
86104
}
87105
group.bench_function("routefinder", |b| {
88106
b.iter(|| {
89-
for route in black_box(call()) {
90-
black_box(routefinder.best_match(route).unwrap());
107+
for path in black_box(&paths) {
108+
let result = black_box(routefinder.best_match(path).unwrap());
109+
assert!(*result.handler());
91110
}
92111
});
93112
});
@@ -98,15 +117,18 @@ fn compare_routers(c: &mut Criterion) {
98117
criterion_group!(benches, compare_routers);
99118
criterion_main!(benches);
100119

101-
macro_rules! register {
120+
macro_rules! routes {
121+
(literal) => {{
122+
routes!(finish => "p1", "p2", "p3", "p4")
123+
}};
102124
(colon) => {{
103-
register!(finish => ":p1", ":p2", ":p3", ":p4")
125+
routes!(finish => ":p1", ":p2", ":p3", ":p4")
104126
}};
105127
(brackets) => {{
106-
register!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
128+
routes!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
107129
}};
108130
(regex) => {{
109-
register!(finish => "(.*)", "(.*)", "(.*)", "(.*)")
131+
routes!(finish => "(.*)", "(.*)", "(.*)", "(.*)")
110132
}};
111133
(finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
112134
[
@@ -169,13 +191,13 @@ macro_rules! register {
169191
concat!("/user/orgs"),
170192
concat!("/orgs/", $p1),
171193
concat!("/orgs/", $p1, "/members"),
172-
concat!("/orgs/", $p1, "/members", $p2),
194+
concat!("/orgs/", $p1, "/members/", $p2),
173195
concat!("/orgs/", $p1, "/public_members"),
174196
concat!("/orgs/", $p1, "/public_members/", $p2),
175197
concat!("/orgs/", $p1, "/teams"),
176198
concat!("/teams/", $p1),
177199
concat!("/teams/", $p1, "/members"),
178-
concat!("/teams/", $p1, "/members", $p2),
200+
concat!("/teams/", $p1, "/members/", $p2),
179201
concat!("/teams/", $p1, "/repos"),
180202
concat!("/teams/", $p1, "/repos/", $p2, "/", $p3),
181203
concat!("/user/teams"),
@@ -204,12 +226,12 @@ macro_rules! register {
204226
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3),
205227
concat!("/repos/", $p1, "/", $p2, "/readme"),
206228
concat!("/repos/", $p1, "/", $p2, "/keys"),
207-
concat!("/repos/", $p1, "/", $p2, "/keys", $p3),
229+
concat!("/repos/", $p1, "/", $p2, "/keys/", $p3),
208230
concat!("/repos/", $p1, "/", $p2, "/downloads"),
209-
concat!("/repos/", $p1, "/", $p2, "/downloads", $p3),
231+
concat!("/repos/", $p1, "/", $p2, "/downloads/", $p3),
210232
concat!("/repos/", $p1, "/", $p2, "/forks"),
211233
concat!("/repos/", $p1, "/", $p2, "/hooks"),
212-
concat!("/repos/", $p1, "/", $p2, "/hooks", $p3),
234+
concat!("/repos/", $p1, "/", $p2, "/hooks/", $p3),
213235
concat!("/repos/", $p1, "/", $p2, "/releases"),
214236
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3),
215237
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3, "/assets"),
@@ -236,12 +258,12 @@ macro_rules! register {
236258
concat!("/users/", $p1, "/following"),
237259
concat!("/user/following"),
238260
concat!("/user/following/", $p1),
239-
concat!("/users/", $p1, "/following", $p2),
261+
concat!("/users/", $p1, "/following/", $p2),
240262
concat!("/users/", $p1, "/keys"),
241263
concat!("/user/keys"),
242264
concat!("/user/keys/", $p1),
243265
]
244266
}};
245267
}
246268

247-
use register;
269+
use routes;

0 commit comments

Comments
 (0)