Experimental benchmarks
This commit is contained in:
parent
ef3720cb67
commit
acd1442cea
9 changed files with 215 additions and 137 deletions
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
# Order is important as this is the order the crates will be released.
|
||||
members = [
|
||||
"juniper_benchmarks",
|
||||
"juniper_codegen",
|
||||
"juniper",
|
||||
"integration_tests/juniper_tests",
|
||||
|
|
127
benches/bench.rs
127
benches/bench.rs
|
@ -1,127 +0,0 @@
|
|||
#[macro_use] extern crate bencher;
|
||||
extern crate juniper;
|
||||
|
||||
use bencher::Bencher;
|
||||
|
||||
use juniper::{execute, RootNode, EmptyMutation, Variables};
|
||||
use juniper::tests::model::Database;
|
||||
|
||||
fn query_type_name(b: &mut Bencher) {
|
||||
let database = Database::new();
|
||||
let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
|
||||
|
||||
let doc = r#"
|
||||
query IntrospectionQueryTypeQuery {
|
||||
__schema {
|
||||
queryType {
|
||||
name
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
b.iter(|| execute(doc, None, &schema, &Variables::new(), &database));
|
||||
}
|
||||
|
||||
fn introspection_query(b: &mut Bencher) {
|
||||
let database = Database::new();
|
||||
let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
|
||||
|
||||
let doc = r#"
|
||||
query IntrospectionQuery {
|
||||
__schema {
|
||||
queryType { name }
|
||||
mutationType { name }
|
||||
subscriptionType { name }
|
||||
types {
|
||||
...FullType
|
||||
}
|
||||
directives {
|
||||
name
|
||||
description
|
||||
locations
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment FullType on __Type {
|
||||
kind
|
||||
name
|
||||
description
|
||||
fields(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
type {
|
||||
...TypeRef
|
||||
}
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
inputFields {
|
||||
...InputValue
|
||||
}
|
||||
interfaces {
|
||||
...TypeRef
|
||||
}
|
||||
enumValues(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
possibleTypes {
|
||||
...TypeRef
|
||||
}
|
||||
}
|
||||
|
||||
fragment InputValue on __InputValue {
|
||||
name
|
||||
description
|
||||
type { ...TypeRef }
|
||||
defaultValue
|
||||
}
|
||||
|
||||
fragment TypeRef on __Type {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
b.iter(|| execute(doc, None, &schema, &Variables::new(), &database));
|
||||
}
|
||||
|
||||
benchmark_group!(queries, query_type_name, introspection_query);
|
||||
benchmark_main!(queries);
|
|
@ -111,13 +111,7 @@ where
|
|||
{
|
||||
let op = self.operation_name();
|
||||
let vars = &self.variables();
|
||||
let res = crate::execute_async(
|
||||
&self.query,
|
||||
op,
|
||||
root_node,
|
||||
vars,
|
||||
context,
|
||||
).await;
|
||||
let res = crate::execute_async(&self.query, op, root_node, vars, context).await;
|
||||
GraphQLResponse(res)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,6 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
|
|||
|
||||
#![doc(html_root_url = "https://docs.rs/juniper/0.13.1")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![cfg_attr(feature = "async", feature(async_await, async_closure))]
|
||||
|
||||
#[doc(hidden)]
|
||||
|
|
3
juniper_benchmarks/.gitignore
vendored
Normal file
3
juniper_benchmarks/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
19
juniper_benchmarks/Cargo.toml
Normal file
19
juniper_benchmarks/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "juniper_benchmarks"
|
||||
version = "0.1.0"
|
||||
authors = ["Christoph Herzog <chris@theduke.at>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
juniper = { path = "../juniper", features = ["async"] }
|
||||
futures-preview = "0.3.0-alpha.18"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.2.11"
|
||||
tokio = "0.2.0-alpha.2"
|
74
juniper_benchmarks/benches/benchmark.rs
Normal file
74
juniper_benchmarks/benches/benchmark.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
extern crate juniper_benchmarks;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion, ParameterizedBenchmark};
|
||||
|
||||
use juniper::{graphql_value, InputValue, ToInputValue, Value};
|
||||
use juniper_benchmarks as j;
|
||||
|
||||
fn bench_sync_vs_async_single_user_flat_instant(c: &mut Criterion) {
|
||||
const QUERY: &'static str = r#"
|
||||
query Query($id: Int) {
|
||||
user(id: $id) {
|
||||
id
|
||||
kind
|
||||
username
|
||||
email
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
c.bench(
|
||||
"Sync vs Async - Single User Flat - Instant",
|
||||
ParameterizedBenchmark::new(
|
||||
"Sync",
|
||||
|b, count| {
|
||||
let ids = (0..*count)
|
||||
.map(|x| InputValue::scalar(x as i32))
|
||||
.collect::<Vec<_>>();
|
||||
let ids = InputValue::list(ids);
|
||||
b.iter(|| {
|
||||
j::execute(
|
||||
QUERY,
|
||||
vec![("ids".to_string(), ids.clone())].into_iter().collect(),
|
||||
)
|
||||
})
|
||||
},
|
||||
vec![1, 10],
|
||||
)
|
||||
.with_function("Async - Single Thread", |b, count| {
|
||||
let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
|
||||
|
||||
let ids = (0..*count)
|
||||
.map(|x| InputValue::scalar(x as i32))
|
||||
.collect::<Vec<_>>();
|
||||
let ids = InputValue::list(ids);
|
||||
|
||||
b.iter(|| {
|
||||
let f = j::execute_async(
|
||||
QUERY,
|
||||
vec![("ids".to_string(), ids.clone())].into_iter().collect(),
|
||||
);
|
||||
rt.block_on(f)
|
||||
})
|
||||
})
|
||||
.with_function("Async - Threadpool", |b, count| {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
let ids = (0..*count)
|
||||
.map(|x| InputValue::scalar(x as i32))
|
||||
.collect::<Vec<_>>();
|
||||
let ids = InputValue::list(ids);
|
||||
|
||||
b.iter(|| {
|
||||
let f = j::execute_async(
|
||||
QUERY,
|
||||
vec![("ids".to_string(), ids.clone())].into_iter().collect(),
|
||||
);
|
||||
rt.block_on(f)
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_sync_vs_async_single_user_flat_instant);
|
||||
criterion_main!(benches);
|
116
juniper_benchmarks/src/lib.rs
Normal file
116
juniper_benchmarks/src/lib.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
#![feature(async_await, async_closure)]
|
||||
|
||||
use juniper::{
|
||||
object, DefaultScalarValue, ExecutionError, FieldError, GraphQLEnum, Value, Variables,
|
||||
};
|
||||
|
||||
pub type QueryResult = Result<
|
||||
(
|
||||
Value<DefaultScalarValue>,
|
||||
Vec<ExecutionError<DefaultScalarValue>>,
|
||||
),
|
||||
String,
|
||||
>;
|
||||
|
||||
pub struct Context {}
|
||||
|
||||
impl Context {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl juniper::Context for Context {}
|
||||
|
||||
#[derive(GraphQLEnum)]
|
||||
pub enum Gender {
|
||||
Male,
|
||||
Female,
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(GraphQLEnum)]
|
||||
pub enum UserKind {
|
||||
SuperAdmin,
|
||||
Admin,
|
||||
Moderator,
|
||||
User,
|
||||
Guest,
|
||||
}
|
||||
|
||||
pub struct User {
|
||||
pub id: i32,
|
||||
pub kind: UserKind,
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
pub gender: Option<Gender>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
fn new(id: i32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
kind: UserKind::Admin,
|
||||
username: "userx".to_string(),
|
||||
email: "userx@domain.com".to_string(),
|
||||
gender: Some(Gender::Female),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[object(Context = Context)]
|
||||
impl User {}
|
||||
|
||||
pub struct Query;
|
||||
|
||||
#[object(Context = Context)]
|
||||
impl Query {
|
||||
fn user_sync_instant(id: i32) -> Result<User, FieldError> {
|
||||
Ok(User::new(id))
|
||||
}
|
||||
|
||||
fn users_sync_instant(ids: Option<Vec<i32>>) -> Result<Vec<User>, FieldError> {
|
||||
if let Some(ids) = ids {
|
||||
let users = ids.into_iter().map(User::new).collect();
|
||||
Ok(users)
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
async fn user_async_instant(id: i32) -> Result<User, FieldError> {
|
||||
Ok(User::new(id))
|
||||
}
|
||||
|
||||
async fn users_async_instant(ids: Option<Vec<i32>>) -> Result<Vec<User>, FieldError> {
|
||||
if let Some(ids) = ids {
|
||||
let users = ids.into_iter().map(User::new).collect();
|
||||
Ok(users)
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mutation;
|
||||
|
||||
#[object(Context = Context)]
|
||||
impl Mutation {}
|
||||
|
||||
pub fn new_schema() -> juniper::RootNode<'static, Query, Mutation> {
|
||||
juniper::RootNode::new(Query, Mutation)
|
||||
}
|
||||
|
||||
pub fn execute(query: &str, vars: Variables) -> QueryResult {
|
||||
let root = new_schema();
|
||||
let ctx = Context::new();
|
||||
juniper::execute(query, None, &root, &vars, &ctx).map_err(|e| format!("{:?}", e))
|
||||
}
|
||||
|
||||
pub async fn execute_async(query: &str, vars: Variables) -> QueryResult {
|
||||
let root = new_schema();
|
||||
let ctx = Context::new();
|
||||
juniper::execute_async(query, None, &root, &vars, &ctx)
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
}
|
|
@ -39,7 +39,6 @@ Check the LICENSE file for details.
|
|||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/juniper_warp/0.2.0")]
|
||||
|
||||
#![cfg_attr(feature = "async", feature(async_await, async_closure))]
|
||||
|
||||
use futures::{future::poll_fn, Future};
|
||||
|
@ -114,7 +113,7 @@ where
|
|||
.iter()
|
||||
.map(|request| request.execute_async(root_node, context))
|
||||
.collect::<Vec<_>>();
|
||||
let responses = futures03::future::join_all(futures).await;
|
||||
let responses = futures03::future::join_all(futures).await;
|
||||
|
||||
GraphQLBatchResponse::Batch(responses)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue