From f60f9ebe254f94a5ec1e4f657882200558ff06e8 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 16 Jul 2021 23:47:41 +0100 Subject: [PATCH] remove either crate and add benchmark --- Cargo.toml | 5 + actix-router/Cargo.toml | 6 +- actix-router/benches/router.rs | 194 +++++++++++++++++++++++++++++++++ actix-router/src/lib.rs | 37 ++++--- actix-router/src/resource.rs | 11 +- 5 files changed, 231 insertions(+), 22 deletions(-) create mode 100644 actix-router/benches/router.rs diff --git a/Cargo.toml b/Cargo.toml index 5bf72300..6ed9b50a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,3 +27,8 @@ actix-utils = { path = "actix-utils" } bytestring = { path = "bytestring" } local-channel = { path = "local-channel" } local-waker = { path = "local-waker" } + +[profile.release] +lto = true +opt-level = 3 +codegen-units = 1 diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index b0bea9b6..02bd99ad 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -21,7 +21,6 @@ default = ["http"] [dependencies] bytestring = ">=0.1.5, <2" -either = "1.6" http = { version = "0.2.3", optional = true } log = "0.4" regex = "1.5" @@ -30,3 +29,8 @@ serde = "1" [dev-dependencies] http = "0.2.3" serde = { version = "1", features = ["derive"] } +criterion = { version = "0.3", features = ["html_reports"] } + +[[bench]] +name = "router" +harness = false diff --git a/actix-router/benches/router.rs b/actix-router/benches/router.rs new file mode 100644 index 00000000..a428b9f1 --- /dev/null +++ b/actix-router/benches/router.rs @@ -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 { + 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::::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); diff --git a/actix-router/src/lib.rs b/actix-router/src/lib.rs index 72b03900..53ae9db6 100644 --- a/actix-router/src/lib.rs +++ b/actix-router/src/lib.rs @@ -4,8 +4,6 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -use either::Either; - mod de; mod path; 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), +} + /// Helper trait for type that could be converted to one or more path pattern. pub trait IntoPatterns { - fn patterns(&self) -> Either>; + fn patterns(&self) -> Patterns; } impl IntoPatterns for String { - fn patterns(&self) -> Either> { - Either::Left(self.clone()) + fn patterns(&self) -> Patterns { + Patterns::Single(self.clone()) } } impl<'a> IntoPatterns for &'a String { - fn patterns(&self) -> Either> { - Either::Left((*self).clone()) + fn patterns(&self) -> Patterns { + Patterns::Single((*self).clone()) } } impl<'a> IntoPatterns for &'a str { - fn patterns(&self) -> Either> { - Either::Left((*self).to_owned()) + fn patterns(&self) -> Patterns { + Patterns::Single((*self).to_owned()) } } impl> IntoPatterns for Vec { - fn patterns(&self) -> Either> { + fn patterns(&self) -> Patterns { let mut patterns = self.iter().map(|v| v.as_ref().to_owned()); match patterns.size_hint() { - (1, _) => Either::Left(patterns.next().unwrap()), - _ => Either::Right(patterns.collect()), + (1, _) => Patterns::Single(patterns.next().unwrap()), + _ => Patterns::List(patterns.collect()), } } } macro_rules! array_patterns_single (($tp:ty) => { impl IntoPatterns for [$tp; 1] { - fn patterns(&self) -> Either> { - Either::Left(self[0].to_owned()) + fn patterns(&self) -> Patterns { + 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 $( impl IntoPatterns for [$tp; $num] { - fn patterns(&self) -> Either> { - Either::Right(self.iter().map($str_fn).collect()) + fn patterns(&self) -> Patterns { + Patterns::List(self.iter().map($str_fn).collect()) } } )+ diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index 3d402c1b..1d7aac65 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -6,11 +6,12 @@ use std::{ mem, }; -use either::Either; use regex::{escape, Regex, RegexSet}; -use crate::path::{Path, PathItem}; -use crate::{IntoPatterns, Resource, ResourcePath}; +use crate::{ + path::{Path, PathItem}, + IntoPatterns, Patterns, Resource, ResourcePath, +}; const MAX_DYNAMIC_SEGMENTS: usize = 16; @@ -78,9 +79,9 @@ impl ResourceDef { /// Panics if path pattern is malformed. pub fn new(path: T) -> Self { 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() { // since zero length pattern sets are possible, return a useless `ResourceDef`