From 547c653d2834e7ff4546f3aa4f0d9100324411b0 Mon Sep 17 00:00:00 2001 From: Ivan Plesskih Date: Tue, 2 Jun 2020 17:57:07 +0500 Subject: [PATCH] Added new benchmark --- README.md | 2 + benchmark/Cargo.toml | 12 ++ benchmark/benches/chat.rs | 13 ++ benchmark/benches/simple.rs | 19 +-- benchmark/chat/Cargo.toml | 9 ++ benchmark/chat/src/lib.rs | 249 ++++++++++++++++++++++++++++++++++++ benchmark/simple/Cargo.toml | 6 +- benchmark/simple/src/lib.rs | 33 +---- benchmark/src/lib.rs | 32 +++++ 9 files changed, 334 insertions(+), 41 deletions(-) create mode 100644 benchmark/benches/chat.rs create mode 100644 benchmark/chat/Cargo.toml create mode 100644 benchmark/chat/src/lib.rs create mode 100644 benchmark/src/lib.rs diff --git a/README.md b/README.md index 05395b23..264f12ec 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ cargo bench Now HTML report is available at `benchmark/target/criterion/report` +Read more here: https://bheisler.github.io/criterion.rs/book/criterion_rs.html + ## Features * Fully support async/await diff --git a/benchmark/Cargo.toml b/benchmark/Cargo.toml index 3097987b..3c082b65 100644 --- a/benchmark/Cargo.toml +++ b/benchmark/Cargo.toml @@ -4,10 +4,22 @@ version = "1.14.11" authors = ["sunli "] edition = "2018" +[dependencies] +async-std = { version = "1.5.0", features = ["attributes"] } +futures = "0.3.4" +serde_json = "*" +async-graphql-parser = { path = "../async-graphql-parser" } +async-graphql = { path = ".." } + [dev-dependencies] criterion = "0.3" simple = { path = "simple" } +chat = { path = "chat" } [[bench]] name = "simple" harness = false + +[[bench]] +name = "chat" +harness = false \ No newline at end of file diff --git a/benchmark/benches/chat.rs b/benchmark/benches/chat.rs new file mode 100644 index 00000000..164a3e60 --- /dev/null +++ b/benchmark/benches/chat.rs @@ -0,0 +1,13 @@ +use chat::{Q, S}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use graphql_benchmark::{parse, run, serialize, GQLResponse}; + +pub fn bench(c: &mut Criterion) { + c.bench_function("chat run", |b| b.iter(|| run(&S, black_box(Q)))); + c.bench_function("chat parse", |b| b.iter(|| parse(black_box(Q)))); + let res = GQLResponse(Ok(run(&S, Q))); + c.bench_function("chat serialize", |b| b.iter(|| serialize(black_box(&res)))); +} + +criterion_group!(chat, bench); +criterion_main!(chat); diff --git a/benchmark/benches/simple.rs b/benchmark/benches/simple.rs index 4a85689c..fc8ba3f3 100644 --- a/benchmark/benches/simple.rs +++ b/benchmark/benches/simple.rs @@ -1,12 +1,15 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use simple::{parse, run, serialize, GQLResponse, Q}; +use graphql_benchmark::{parse, run, serialize, GQLResponse}; +use simple::{Q, S}; -pub fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("run", |b| b.iter(|| run(black_box(Q)))); - c.bench_function("parse", |b| b.iter(|| parse(black_box(Q)))); - let res = GQLResponse(run(Q)); - c.bench_function("serialize", |b| b.iter(|| serialize(black_box(&res)))); +pub fn bench(c: &mut Criterion) { + c.bench_function("simple run", |b| b.iter(|| run(&S, black_box(Q)))); + c.bench_function("simple parse", |b| b.iter(|| parse(black_box(Q)))); + let res = GQLResponse(Ok(run(&S, Q))); + c.bench_function("simple serialize", |b| { + b.iter(|| serialize(black_box(&res))) + }); } -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); +criterion_group!(simple, bench); +criterion_main!(simple); diff --git a/benchmark/chat/Cargo.toml b/benchmark/chat/Cargo.toml new file mode 100644 index 00000000..609fdf2e --- /dev/null +++ b/benchmark/chat/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "chat" +version = "0.1.0" +authors = ["Ivan Plesskih "] +edition = "2018" + +[dependencies] +async-graphql = { path = "../.." } +lazy_static = "*" \ No newline at end of file diff --git a/benchmark/chat/src/lib.rs b/benchmark/chat/src/lib.rs new file mode 100644 index 00000000..cf8c49ad --- /dev/null +++ b/benchmark/chat/src/lib.rs @@ -0,0 +1,249 @@ +use async_graphql::*; + +pub struct ChatData { + pub id: String, + pub created_at: String, + pub title: String, + pub caption: String, + pub creator_user_id: String, + pub state: String, +} + +pub struct UserData { + pub id: String, + pub is_operator: bool, + pub phone: u64, + pub join_date: String, + + pub state: String, +} + +pub struct ProfileData { + pub first_name: String, + pub last_name: String, + pub city: Option, + pub job_title: Option, + pub email: String, +} + +pub struct MessageData { + pub id: String, + pub user_id: String, + pub timestamp: String, + pub edited: bool, + pub order: i32, + pub message: String, +} + +lazy_static::lazy_static! { + pub static ref CHAT: ChatData = ChatData { + id: "1".to_string(), + created_at: "today".to_string(), + title: "chat".to_string(), + caption: "asdasd".to_string(), + creator_user_id: "123".to_string(), + state: "ACTIVE".to_string(), + }; + + pub static ref USER: UserData = UserData { + id: "123".to_string(), + is_operator: false, + phone: 79123273936, + join_date: "today".to_string(), + state: "ACTIVE".to_string(), + }; + + pub static ref PROFILE: ProfileData = ProfileData { + first_name: "Ivan".to_string(), + last_name: "Plesskih".to_string(), + city: Some("Che".to_string()), + job_title: Some("progr".to_string()), + email: "asd@qwe.ru".to_string(), + }; + + pub static ref MESSAGE: MessageData = MessageData { + id: "456".to_string(), + user_id: "123".to_string(), + timestamp: "today".to_string(), + edited: false, + order: 123, + message: "Hello, world!".to_string(), + }; +} + +pub struct Chat; + +#[Object] +impl Chat { + pub async fn id(&self) -> ID { + ID::from(&CHAT.id) + } + + pub async fn messages(&self) -> Vec { + let mut res = vec![]; + for _ in 0..30 { + res.push(Message); + } + res + } + + pub async fn users(&self) -> Vec { + let mut res = vec![]; + for _ in 0..5 { + res.push(User); + } + res + } + + pub async fn creator(&self) -> User { + User + } + + #[field(name = "created_at")] + pub async fn created_at(&self) -> &String { + &CHAT.created_at + } + pub async fn title(&self) -> &String { + &CHAT.title + } + pub async fn caption(&self) -> &String { + &CHAT.caption + } + pub async fn state(&self) -> &String { + &CHAT.state + } +} + +pub struct Message; + +#[Object] +impl Message { + pub async fn id(&self) -> ID { + ID::from(&MESSAGE.id) + } + + pub async fn user(&self) -> User { + User + } + pub async fn timestamp(&self) -> &String { + &MESSAGE.timestamp + } + pub async fn message(&self) -> &String { + &MESSAGE.message + } + pub async fn order(&self) -> i32 { + MESSAGE.order + } + pub async fn edited(&self) -> bool { + MESSAGE.edited + } +} + +pub struct User; + +#[Object] +impl User { + pub async fn id(&self) -> ID { + ID::from(&USER.id) + } + + pub async fn profile(&self) -> Option { + Some(UserProfile) + } + + #[field(name = "is_operator")] + pub async fn is_operator(&self) -> bool { + USER.is_operator + } + pub async fn phone(&self) -> String { + USER.phone.to_string() + } + #[field(name = "join_date")] + pub async fn join_date(&self) -> &String { + &USER.join_date + } + pub async fn state(&self) -> &String { + &USER.state + } +} + +pub struct UserProfile; + +#[Object] +impl UserProfile { + pub async fn email(&self) -> &String { + &PROFILE.email + } + #[field(name = "first_name")] + pub async fn first_name(&self) -> &String { + &PROFILE.first_name + } + #[field(name = "last_name")] + pub async fn last_name(&self) -> &String { + &PROFILE.last_name + } + #[field(name = "job_title")] + pub async fn job_title(&self) -> &Option { + &PROFILE.job_title + } + pub async fn city(&self) -> &Option { + &PROFILE.city + } +} + +pub struct Query; + +#[Object] +impl Query { + async fn chats(&self) -> Vec { + let mut res = vec![]; + for _ in 0..30 { + res.push(Chat); + } + res + } +} + +lazy_static::lazy_static! { + pub static ref S: Schema = Schema::new(Query, EmptyMutation, EmptySubscription); +} + +pub const Q: &str = r#" +fragment User on User { + id + is_operator + phone + join_date + state + profile { + email + first_name + last_name + job_title + city + } +} + +{ + chats { + id + created_at + title + caption + state + creator { + ...User + } + messages { + id + timestamp + edited + message + order + } + users { + ...User + } + } +} +"#; diff --git a/benchmark/simple/Cargo.toml b/benchmark/simple/Cargo.toml index 958e9388..3e29a77a 100644 --- a/benchmark/simple/Cargo.toml +++ b/benchmark/simple/Cargo.toml @@ -6,8 +6,4 @@ edition = "2018" [dependencies] async-graphql = { path = "../.." } -async-std = { version = "1.5.0", features = ["attributes"] } -futures = "0.3.4" -serde_json = "*" -lazy_static = "*" -async-graphql-parser = { path = "../../async-graphql-parser" } +lazy_static = "*" \ No newline at end of file diff --git a/benchmark/simple/src/lib.rs b/benchmark/simple/src/lib.rs index d7e89fdc..6fb7f20d 100644 --- a/benchmark/simple/src/lib.rs +++ b/benchmark/simple/src/lib.rs @@ -1,7 +1,4 @@ use async_graphql::*; -use async_graphql_parser::{parse_query, query::Document}; -use async_std::task; -pub use http::GQLResponse; pub struct QueryRoot; @@ -38,6 +35,11 @@ impl MyObj { } } +lazy_static::lazy_static! { + pub static ref S: Schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + // static ref D: Document = parse_query(Q).unwrap(); +} + pub const Q: &str = r#"{ valueI32 obj { valueI32 valueList obj { @@ -71,28 +73,3 @@ pub const Q: &str = r#"{ } } }"#; - -lazy_static::lazy_static! { - static ref S: Schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); - // static ref D: Document = parse_query(Q).unwrap(); -} - -pub fn run(q: &str) -> Result { - task::block_on(async { S.execute(q).await }) -} - -pub fn parse(q: &str) -> Document { - parse_query(q).unwrap() -} - -// pub fn validate() { -// check_rules(&S.env.registry, &D, S.validation_mode).unwrap(); -// } -// -// pub fn resolve() { -// do_resolve(...).unwrap(); -// } - -pub fn serialize(r: &GQLResponse) -> String { - serde_json::to_string(&r).unwrap() -} diff --git a/benchmark/src/lib.rs b/benchmark/src/lib.rs new file mode 100644 index 00000000..bc438f9c --- /dev/null +++ b/benchmark/src/lib.rs @@ -0,0 +1,32 @@ +pub use async_graphql::http::GQLResponse; +use async_graphql::{ObjectType, QueryResponse, Schema, SubscriptionType}; +use async_graphql_parser::{parse_query, query::Document}; +use async_std::task; + +pub fn run( + s: &Schema, + q: &str, +) -> QueryResponse +where + Query: ObjectType + Send + Sync + 'static, + Mutation: ObjectType + Send + Sync + 'static, + Subscription: SubscriptionType + Send + Sync + 'static, +{ + task::block_on(async { s.execute(q).await.unwrap() }) +} + +pub fn parse(q: &str) -> Document { + parse_query(q).unwrap() +} + +// pub fn validate() { +// check_rules(&S.env.registry, &D, S.validation_mode).unwrap(); +// } +// +// pub fn resolve() { +// do_resolve(...).unwrap(); +// } + +pub fn serialize(r: &GQLResponse) -> String { + serde_json::to_string(&r).unwrap() +}