-
-
Notifications
You must be signed in to change notification settings - Fork 677
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(performance): optimize resolvers execution paths (#488)
Co-authors: @croquiscom * optimize applyMiddlewares for simple case * optimize createBasicFieldResolver * add isPromise * optimize getParams * optimize createHandlerResolver * add benchmark for large array with field resolver @MichalLytek * Add benchmark results * Rename util to isPromiseLike * Merge "large" benchmarks with "array" and update results * Update performance docs * Add entry to changelog
- Loading branch information
1 parent
ba96c58
commit d6b348d
Showing
15 changed files
with
555 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { | ||
GraphQLSchema, | ||
GraphQLObjectType, | ||
GraphQLString, | ||
GraphQLNonNull, | ||
GraphQLBoolean, | ||
GraphQLInt, | ||
GraphQLList, | ||
} from "graphql"; | ||
|
||
import { runBenchmark, ARRAY_ITEMS } from "../run"; | ||
|
||
const SampleObjectType: GraphQLObjectType = new GraphQLObjectType({ | ||
name: "SampleObject", | ||
fields: () => ({ | ||
stringField: { | ||
type: new GraphQLNonNull(GraphQLString), | ||
resolve: async source => { | ||
return source.stringField; | ||
}, | ||
}, | ||
numberField: { | ||
type: new GraphQLNonNull(GraphQLInt), | ||
resolve: async source => { | ||
return source.numberField; | ||
}, | ||
}, | ||
booleanField: { | ||
type: new GraphQLNonNull(GraphQLBoolean), | ||
resolve: async source => { | ||
return source.booleanField; | ||
}, | ||
}, | ||
nestedField: { | ||
type: SampleObjectType, | ||
resolve: async source => { | ||
return source.nestedField; | ||
}, | ||
}, | ||
}), | ||
}); | ||
|
||
const schema = new GraphQLSchema({ | ||
query: new GraphQLObjectType({ | ||
name: "Query", | ||
fields: { | ||
multipleNestedObjects: { | ||
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(SampleObjectType))), | ||
resolve: () => | ||
Array.from({ length: ARRAY_ITEMS }, (_, index) => ({ | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
nestedField: { | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
}, | ||
})), | ||
}, | ||
}, | ||
}), | ||
}); | ||
|
||
runBenchmark(schema).catch(console.error); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,34 @@ | ||
Core i7 2700K @ 3.5GHz | ||
Windows 10 x64 | ||
10 000 array items | 100 iterations | ||
25 000 array items | 50 iterations | ||
Node.js v13.5 | ||
|
||
----- | ||
Node.js v13.2 | ||
TypeGraphQL | ||
|
||
standard | ||
- 42.551s | ||
- 15.518s | ||
|
||
using sync field resolvers | ||
- 18.180s | ||
|
||
using async field resolvers | ||
- 39.934s | ||
|
||
using getters | ||
- 31.207s | ||
|
||
standard with global middleware | ||
- 62.664s | ||
|
||
with `simpleResolvers: true` | ||
- 11.841s | ||
- 14.980s | ||
|
||
----- | ||
`graphql-js` | ||
|
||
standard | ||
- 13.276s | ||
|
||
graphql-js | ||
- 9.963s | ||
async field resolvers | ||
- 25.630s |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import "reflect-metadata"; | ||
import { | ||
buildSchema, | ||
Field, | ||
ObjectType, | ||
Resolver, | ||
Query, | ||
Int, | ||
FieldResolver, | ||
Root, | ||
} from "../../../build/package/dist"; | ||
|
||
import { runBenchmark, ARRAY_ITEMS } from "../run"; | ||
|
||
@ObjectType() | ||
class SampleObject { | ||
@Field() | ||
stringField!: string; | ||
|
||
@Field(type => Int) | ||
numberField!: number; | ||
|
||
@Field() | ||
booleanField!: boolean; | ||
|
||
@Field({ nullable: true }) | ||
nestedField?: SampleObject; | ||
} | ||
|
||
@Resolver(SampleObject) | ||
class SampleResolver { | ||
@Query(returns => [SampleObject]) | ||
multipleNestedObjects(): SampleObject[] { | ||
return Array.from( | ||
{ length: ARRAY_ITEMS }, | ||
(_, index): SampleObject => ({ | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
nestedField: { | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
}, | ||
}), | ||
); | ||
} | ||
|
||
@FieldResolver() | ||
async stringField(@Root() source: SampleObject) { | ||
return source.stringField; | ||
} | ||
|
||
@FieldResolver() | ||
async numberField(@Root() source: SampleObject) { | ||
return source.numberField; | ||
} | ||
|
||
@FieldResolver() | ||
async booleanField(@Root() source: SampleObject) { | ||
return source.booleanField; | ||
} | ||
|
||
@FieldResolver() | ||
async nestedField(@Root() source: SampleObject) { | ||
return source.nestedField; | ||
} | ||
} | ||
|
||
async function main() { | ||
const schema = await buildSchema({ | ||
resolvers: [SampleResolver], | ||
}); | ||
|
||
await runBenchmark(schema); | ||
} | ||
|
||
main().catch(console.error); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import "reflect-metadata"; | ||
import { | ||
buildSchema, | ||
Field, | ||
ObjectType, | ||
Resolver, | ||
Query, | ||
Int, | ||
MiddlewareFn, | ||
} from "../../../build/package/dist"; | ||
|
||
import { runBenchmark, ARRAY_ITEMS } from "../run"; | ||
|
||
@ObjectType({ simpleResolvers: true }) | ||
class SampleObject { | ||
@Field() | ||
stringField!: string; | ||
|
||
@Field(type => Int) | ||
numberField!: number; | ||
|
||
@Field() | ||
booleanField!: boolean; | ||
|
||
@Field({ nullable: true }) | ||
nestedField?: SampleObject; | ||
} | ||
|
||
@Resolver() | ||
class SampleResolver { | ||
@Query(returns => [SampleObject]) | ||
multipleNestedObjects(): SampleObject[] { | ||
return Array.from( | ||
{ length: ARRAY_ITEMS }, | ||
(_, index): SampleObject => ({ | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
nestedField: { | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
}, | ||
}), | ||
); | ||
} | ||
} | ||
|
||
const log = (...args: any[]) => void 0; // noop | ||
|
||
const loggingMiddleware: MiddlewareFn = ({ info }, next) => { | ||
log(`${info.parentType.name}.${info.fieldName} accessed`); | ||
return next(); | ||
}; | ||
|
||
async function main() { | ||
const schema = await buildSchema({ | ||
resolvers: [SampleResolver], | ||
globalMiddlewares: [loggingMiddleware], | ||
}); | ||
|
||
await runBenchmark(schema); | ||
} | ||
|
||
main().catch(console.error); |
6 changes: 3 additions & 3 deletions
6
benchmarks/array/type-graphql.ts → benchmarks/array/type-graphql/standard.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import "reflect-metadata"; | ||
import { | ||
buildSchema, | ||
Field, | ||
ObjectType, | ||
Resolver, | ||
Query, | ||
Int, | ||
FieldResolver, | ||
Root, | ||
} from "../../../build/package/dist"; | ||
|
||
import { runBenchmark, ARRAY_ITEMS } from "../run"; | ||
|
||
@ObjectType() | ||
class SampleObject { | ||
@Field() | ||
stringField!: string; | ||
|
||
@Field(type => Int) | ||
numberField!: number; | ||
|
||
@Field() | ||
booleanField!: boolean; | ||
|
||
@Field({ nullable: true }) | ||
nestedField?: SampleObject; | ||
} | ||
|
||
@Resolver(SampleObject) | ||
class SampleResolver { | ||
@Query(returns => [SampleObject]) | ||
multipleNestedObjects(): SampleObject[] { | ||
return Array.from( | ||
{ length: ARRAY_ITEMS }, | ||
(_, index): SampleObject => ({ | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
nestedField: { | ||
stringField: "stringField", | ||
booleanField: true, | ||
numberField: index, | ||
}, | ||
}), | ||
); | ||
} | ||
|
||
@FieldResolver() | ||
stringField(@Root() source: SampleObject) { | ||
return source.stringField; | ||
} | ||
|
||
@FieldResolver() | ||
numberField(@Root() source: SampleObject) { | ||
return source.numberField; | ||
} | ||
|
||
@FieldResolver() | ||
booleanField(@Root() source: SampleObject) { | ||
return source.booleanField; | ||
} | ||
|
||
@FieldResolver() | ||
nestedField(@Root() source: SampleObject) { | ||
return source.nestedField; | ||
} | ||
} | ||
|
||
async function main() { | ||
const schema = await buildSchema({ | ||
resolvers: [SampleResolver], | ||
}); | ||
|
||
await runBenchmark(schema); | ||
} | ||
|
||
main().catch(console.error); |
Oops, something went wrong.