remove either crate and add benchmark

This commit is contained in:
Rob Ede 2021-07-16 23:47:41 +01:00
parent 47778bc36a
commit f60f9ebe25
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
5 changed files with 231 additions and 22 deletions

View File

@ -27,3 +27,8 @@ actix-utils = { path = "actix-utils" }
bytestring = { path = "bytestring" } bytestring = { path = "bytestring" }
local-channel = { path = "local-channel" } local-channel = { path = "local-channel" }
local-waker = { path = "local-waker" } local-waker = { path = "local-waker" }
[profile.release]
lto = true
opt-level = 3
codegen-units = 1

View File

@ -21,7 +21,6 @@ default = ["http"]
[dependencies] [dependencies]
bytestring = ">=0.1.5, <2" bytestring = ">=0.1.5, <2"
either = "1.6"
http = { version = "0.2.3", optional = true } http = { version = "0.2.3", optional = true }
log = "0.4" log = "0.4"
regex = "1.5" regex = "1.5"
@ -30,3 +29,8 @@ serde = "1"
[dev-dependencies] [dev-dependencies]
http = "0.2.3" http = "0.2.3"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
criterion = { version = "0.3", features = ["html_reports"] }
[[bench]]
name = "router"
harness = false

View File

@ -0,0 +1,194 @@
//! Based on https://github.com/ibraheemdev/matchit/blob/master/benches/bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
macro_rules! register {
(colon) => {{
register!(finish => ":p1", ":p2", ":p3", ":p4")
}};
(brackets) => {{
register!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
}};
(regex) => {{
register!(finish => "(.*)", "(.*)", "(.*)", "(.*)")
}};
(finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
let arr = [
concat!("/authorizations"),
concat!("/authorizations/", $p1),
concat!("/applications/", $p1, "/tokens/", $p2),
concat!("/events"),
concat!("/repos/", $p1, "/", $p2, "/events"),
concat!("/networks/", $p1, "/", $p2, "/events"),
concat!("/orgs/", $p1, "/events"),
concat!("/users/", $p1, "/received_events"),
concat!("/users/", $p1, "/received_events/public"),
concat!("/users/", $p1, "/events"),
concat!("/users/", $p1, "/events/public"),
concat!("/users/", $p1, "/events/orgs/", $p2),
concat!("/feeds"),
concat!("/notifications"),
concat!("/repos/", $p1, "/", $p2, "/notifications"),
concat!("/notifications/threads/", $p1),
concat!("/notifications/threads/", $p1, "/subscription"),
concat!("/repos/", $p1, "/", $p2, "/stargazers"),
concat!("/users/", $p1, "/starred"),
concat!("/user/starred"),
concat!("/user/starred/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/subscribers"),
concat!("/users/", $p1, "/subscriptions"),
concat!("/user/subscriptions"),
concat!("/repos/", $p1, "/", $p2, "/subscription"),
concat!("/user/subscriptions/", $p1, "/", $p2),
concat!("/users/", $p1, "/gists"),
concat!("/gists"),
concat!("/gists/", $p1),
concat!("/gists/", $p1, "/star"),
concat!("/repos/", $p1, "/", $p2, "/git/blobs/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/refs"),
concat!("/repos/", $p1, "/", $p2, "/git/tags/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/trees/", $p3),
concat!("/issues"),
concat!("/user/issues"),
concat!("/orgs/", $p1, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3),
concat!("/repos/", $p1, "/", $p2, "/assignees"),
concat!("/repos/", $p1, "/", $p2, "/assignees/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/events"),
concat!("/repos/", $p1, "/", $p2, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/labels/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3),
concat!("/emojis"),
concat!("/gitignore/templates"),
concat!("/gitignore/templates/", $p1),
concat!("/meta"),
concat!("/rate_limit"),
concat!("/users/", $p1, "/orgs"),
concat!("/user/orgs"),
concat!("/orgs/", $p1),
concat!("/orgs/", $p1, "/members"),
concat!("/orgs/", $p1, "/members", $p2),
concat!("/orgs/", $p1, "/public_members"),
concat!("/orgs/", $p1, "/public_members/", $p2),
concat!("/orgs/", $p1, "/teams"),
concat!("/teams/", $p1),
concat!("/teams/", $p1, "/members"),
concat!("/teams/", $p1, "/members", $p2),
concat!("/teams/", $p1, "/repos"),
concat!("/teams/", $p1, "/repos/", $p2, "/", $p3),
concat!("/user/teams"),
concat!("/repos/", $p1, "/", $p2, "/pulls"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/files"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/merge"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/comments"),
concat!("/user/repos"),
concat!("/users/", $p1, "/repos"),
concat!("/orgs/", $p1, "/repos"),
concat!("/repositories"),
concat!("/repos/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/contributors"),
concat!("/repos/", $p1, "/", $p2, "/languages"),
concat!("/repos/", $p1, "/", $p2, "/teams"),
concat!("/repos/", $p1, "/", $p2, "/tags"),
concat!("/repos/", $p1, "/", $p2, "/branches"),
concat!("/repos/", $p1, "/", $p2, "/branches/", $p3),
concat!("/repos/", $p1, "/", $p2, "/collaborators"),
concat!("/repos/", $p1, "/", $p2, "/collaborators/", $p3),
concat!("/repos/", $p1, "/", $p2, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/readme"),
concat!("/repos/", $p1, "/", $p2, "/keys"),
concat!("/repos/", $p1, "/", $p2, "/keys", $p3),
concat!("/repos/", $p1, "/", $p2, "/downloads"),
concat!("/repos/", $p1, "/", $p2, "/downloads", $p3),
concat!("/repos/", $p1, "/", $p2, "/forks"),
concat!("/repos/", $p1, "/", $p2, "/hooks"),
concat!("/repos/", $p1, "/", $p2, "/hooks", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases"),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3, "/assets"),
concat!("/repos/", $p1, "/", $p2, "/stats/contributors"),
concat!("/repos/", $p1, "/", $p2, "/stats/commit_activity"),
concat!("/repos/", $p1, "/", $p2, "/stats/code_frequency"),
concat!("/repos/", $p1, "/", $p2, "/stats/participation"),
concat!("/repos/", $p1, "/", $p2, "/stats/punch_card"),
concat!("/repos/", $p1, "/", $p2, "/statuses/", $p3),
concat!("/search/repositories"),
concat!("/search/code"),
concat!("/search/issues"),
concat!("/search/users"),
concat!("/legacy/issues/search/", $p1, "/", $p2, "/", $p3, "/", $p4),
concat!("/legacy/repos/search/", $p1),
concat!("/legacy/user/search/", $p1),
concat!("/legacy/user/email/", $p1),
concat!("/users/", $p1),
concat!("/user"),
concat!("/users"),
concat!("/user/emails"),
concat!("/users/", $p1, "/followers"),
concat!("/user/followers"),
concat!("/users/", $p1, "/following"),
concat!("/user/following"),
concat!("/user/following/", $p1),
concat!("/users/", $p1, "/following", $p2),
concat!("/users/", $p1, "/keys"),
concat!("/user/keys"),
concat!("/user/keys/", $p1),
];
std::array::IntoIter::new(arr)
}};
}
fn call() -> impl Iterator<Item = &'static str> {
let arr = [
"/authorizations",
"/user/repos",
"/repos/rust-lang/rust/stargazers",
"/orgs/rust-lang/public_members/nikomatsakis",
"/repos/rust-lang/rust/releases/1.51.0",
];
std::array::IntoIter::new(arr)
}
fn compare_routers(c: &mut Criterion) {
let mut group = c.benchmark_group("Compare Routers");
let mut actix = actix_router::Router::<bool>::build();
for route in register!(brackets) {
actix.path(route, true);
}
let actix = actix.finish();
group.bench_function("actix", |b| {
b.iter(|| {
for route in call() {
let mut path = actix_router::Path::new(route);
black_box(actix.recognize(&mut path).unwrap());
}
});
});
let regex_set = regex::RegexSet::new(register!(regex)).unwrap();
group.bench_function("regex", |b| {
b.iter(|| {
for route in call() {
black_box(regex_set.matches(route));
}
});
});
group.finish();
}
criterion_group!(benches, compare_routers);
criterion_main!(benches);

View File

@ -4,8 +4,6 @@
#![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use either::Either;
mod de; mod de;
mod path; mod path;
mod resource; mod resource;
@ -42,44 +40,51 @@ impl ResourcePath for bytestring::ByteString {
} }
} }
/// One or many patterns.
#[derive(Debug, Clone)]
pub enum Patterns {
Single(String),
List(Vec<String>),
}
/// Helper trait for type that could be converted to one or more path pattern. /// Helper trait for type that could be converted to one or more path pattern.
pub trait IntoPatterns { pub trait IntoPatterns {
fn patterns(&self) -> Either<String, Vec<String>>; fn patterns(&self) -> Patterns;
} }
impl IntoPatterns for String { impl IntoPatterns for String {
fn patterns(&self) -> Either<String, Vec<String>> { fn patterns(&self) -> Patterns {
Either::Left(self.clone()) Patterns::Single(self.clone())
} }
} }
impl<'a> IntoPatterns for &'a String { impl<'a> IntoPatterns for &'a String {
fn patterns(&self) -> Either<String, Vec<String>> { fn patterns(&self) -> Patterns {
Either::Left((*self).clone()) Patterns::Single((*self).clone())
} }
} }
impl<'a> IntoPatterns for &'a str { impl<'a> IntoPatterns for &'a str {
fn patterns(&self) -> Either<String, Vec<String>> { fn patterns(&self) -> Patterns {
Either::Left((*self).to_owned()) Patterns::Single((*self).to_owned())
} }
} }
impl<T: AsRef<str>> IntoPatterns for Vec<T> { impl<T: AsRef<str>> IntoPatterns for Vec<T> {
fn patterns(&self) -> Either<String, Vec<String>> { fn patterns(&self) -> Patterns {
let mut patterns = self.iter().map(|v| v.as_ref().to_owned()); let mut patterns = self.iter().map(|v| v.as_ref().to_owned());
match patterns.size_hint() { match patterns.size_hint() {
(1, _) => Either::Left(patterns.next().unwrap()), (1, _) => Patterns::Single(patterns.next().unwrap()),
_ => Either::Right(patterns.collect()), _ => Patterns::List(patterns.collect()),
} }
} }
} }
macro_rules! array_patterns_single (($tp:ty) => { macro_rules! array_patterns_single (($tp:ty) => {
impl IntoPatterns for [$tp; 1] { impl IntoPatterns for [$tp; 1] {
fn patterns(&self) -> Either<String, Vec<String>> { fn patterns(&self) -> Patterns {
Either::Left(self[0].to_owned()) Patterns::Single(self[0].to_owned())
} }
} }
}); });
@ -88,8 +93,8 @@ macro_rules! array_patterns_multiple (($tp:ty, $str_fn:expr, $($num:tt) +) => {
// for each array length specified in $num // for each array length specified in $num
$( $(
impl IntoPatterns for [$tp; $num] { impl IntoPatterns for [$tp; $num] {
fn patterns(&self) -> Either<String, Vec<String>> { fn patterns(&self) -> Patterns {
Either::Right(self.iter().map($str_fn).collect()) Patterns::List(self.iter().map($str_fn).collect())
} }
} }
)+ )+

View File

@ -6,11 +6,12 @@ use std::{
mem, mem,
}; };
use either::Either;
use regex::{escape, Regex, RegexSet}; use regex::{escape, Regex, RegexSet};
use crate::path::{Path, PathItem}; use crate::{
use crate::{IntoPatterns, Resource, ResourcePath}; path::{Path, PathItem},
IntoPatterns, Patterns, Resource, ResourcePath,
};
const MAX_DYNAMIC_SEGMENTS: usize = 16; const MAX_DYNAMIC_SEGMENTS: usize = 16;
@ -78,9 +79,9 @@ impl ResourceDef {
/// Panics if path pattern is malformed. /// Panics if path pattern is malformed.
pub fn new<T: IntoPatterns>(path: T) -> Self { pub fn new<T: IntoPatterns>(path: T) -> Self {
match path.patterns() { match path.patterns() {
Either::Left(pattern) => ResourceDef::from_single_pattern(&pattern, false), Patterns::Single(pattern) => ResourceDef::from_single_pattern(&pattern, false),
Either::Right(patterns) => { Patterns::List(patterns) => {
if patterns.is_empty() { if patterns.is_empty() {
// since zero length pattern sets are possible, return a useless `ResourceDef` // since zero length pattern sets are possible, return a useless `ResourceDef`