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]
|
[workspace]
|
||||||
# Order is important as this is the order the crates will be released.
|
# Order is important as this is the order the crates will be released.
|
||||||
members = [
|
members = [
|
||||||
|
"juniper_benchmarks",
|
||||||
"juniper_codegen",
|
"juniper_codegen",
|
||||||
"juniper",
|
"juniper",
|
||||||
"integration_tests/juniper_tests",
|
"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 op = self.operation_name();
|
||||||
let vars = &self.variables();
|
let vars = &self.variables();
|
||||||
let res = crate::execute_async(
|
let res = crate::execute_async(&self.query, op, root_node, vars, context).await;
|
||||||
&self.query,
|
|
||||||
op,
|
|
||||||
root_node,
|
|
||||||
vars,
|
|
||||||
context,
|
|
||||||
).await;
|
|
||||||
GraphQLResponse(res)
|
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")]
|
#![doc(html_root_url = "https://docs.rs/juniper/0.13.1")]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
#![cfg_attr(feature = "async", feature(async_await, async_closure))]
|
#![cfg_attr(feature = "async", feature(async_await, async_closure))]
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[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(missing_docs)]
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![doc(html_root_url = "https://docs.rs/juniper_warp/0.2.0")]
|
#![doc(html_root_url = "https://docs.rs/juniper_warp/0.2.0")]
|
||||||
|
|
||||||
#![cfg_attr(feature = "async", feature(async_await, async_closure))]
|
#![cfg_attr(feature = "async", feature(async_await, async_closure))]
|
||||||
|
|
||||||
use futures::{future::poll_fn, Future};
|
use futures::{future::poll_fn, Future};
|
||||||
|
@ -114,7 +113,7 @@ where
|
||||||
.iter()
|
.iter()
|
||||||
.map(|request| request.execute_async(root_node, context))
|
.map(|request| request.execute_async(root_node, context))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let responses = futures03::future::join_all(futures).await;
|
let responses = futures03::future::join_all(futures).await;
|
||||||
|
|
||||||
GraphQLBatchResponse::Batch(responses)
|
GraphQLBatchResponse::Batch(responses)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue