Skip to content

Commit 66acb73

Browse files
committed
Added first meaningful benchmarks to have some grounds for future performance optimizations
1 parent 34ca931 commit 66acb73

File tree

6 files changed

+449
-2
lines changed

6 files changed

+449
-2
lines changed

benchmarks/HugeSchemaBench.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
namespace GraphQL\Benchmarks;
3+
4+
use GraphQL\GraphQL;
5+
use GraphQL\Schema;
6+
use GraphQL\Benchmarks\Utils\QueryGenerator;
7+
use GraphQL\Benchmarks\Utils\SchemaGenerator;
8+
use GraphQL\Type\LazyResolution;
9+
10+
/**
11+
* @BeforeMethods({"setUp"})
12+
* @OutputTimeUnit("milliseconds", precision=3)
13+
* @Warmup(1)
14+
* @Revs(5)
15+
* @Iterations(1)
16+
*/
17+
class HugeSchemaBench
18+
{
19+
/**
20+
* @var SchemaGenerator
21+
*/
22+
private $schemaBuilder;
23+
24+
/**
25+
* @var array
26+
*/
27+
private $descriptor;
28+
29+
private $schema;
30+
31+
private $lazySchema;
32+
33+
/**
34+
* @var string
35+
*/
36+
private $smallQuery;
37+
38+
public function setUp()
39+
{
40+
$this->schemaBuilder = new SchemaGenerator([
41+
'totalTypes' => 600,
42+
'fieldsPerType' => 8,
43+
'listFieldsPerType' => 2,
44+
'nestingLevel' => 10
45+
]);
46+
47+
$this->schema = $this->schemaBuilder->buildSchema();
48+
49+
$queryBuilder = new QueryGenerator($this->schema, 0.05);
50+
$this->descriptor = $this->schema->getDescriptor();
51+
$this->smallQuery = $queryBuilder->buildQuery();
52+
}
53+
54+
public function benchSchema()
55+
{
56+
$this->schemaBuilder
57+
->buildSchema();
58+
}
59+
60+
public function benchSchemaLazy()
61+
{
62+
$this->createLazySchema();
63+
}
64+
65+
public function benchSmallQuery()
66+
{
67+
$result = GraphQL::execute($this->schema, $this->smallQuery);
68+
}
69+
70+
public function benchSmallQueryLazy()
71+
{
72+
$schema = $this->createLazySchema();
73+
$result = GraphQL::execute($schema, $this->smallQuery);
74+
}
75+
76+
private function createLazySchema()
77+
{
78+
$strategy = new LazyResolution(
79+
$this->descriptor,
80+
function($name) {
81+
return $this->schemaBuilder->loadType($name);
82+
}
83+
);
84+
85+
return new Schema([
86+
'query' => $this->schemaBuilder->buildQueryType(),
87+
'typeResolution' => $strategy,
88+
]);
89+
}
90+
}

benchmarks/LexerBench.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function benchIntrospectionQuery()
2929
$lexer = new Lexer($this->introQuery);
3030

3131
do {
32-
$token = $lexer->nextToken();
32+
$token = $lexer->advance();
3333
} while ($token->kind !== Token::EOF);
3434
}
3535
}

benchmarks/StarWarsBench.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
namespace GraphQL\Benchmarks;
3+
use GraphQL\GraphQL;
4+
use GraphQL\Tests\StarWarsSchema;
5+
use GraphQL\Type\Introspection;
6+
7+
/**
8+
* @BeforeMethods({"setIntroQuery"})
9+
* @OutputTimeUnit("milliseconds", precision=3)
10+
* @Warmup(2)
11+
* @Revs(10)
12+
* @Iterations(2)
13+
*/
14+
class StarWarsBench
15+
{
16+
private $introQuery;
17+
18+
public function setIntroQuery()
19+
{
20+
$this->introQuery = Introspection::getIntrospectionQuery();
21+
}
22+
23+
public function benchSchema()
24+
{
25+
StarWarsSchema::build();
26+
}
27+
28+
public function benchHeroQuery()
29+
{
30+
$q = '
31+
query HeroNameQuery {
32+
hero {
33+
name
34+
}
35+
}
36+
';
37+
38+
GraphQL::execute(
39+
StarWarsSchema::build(),
40+
$q
41+
);
42+
}
43+
44+
public function benchNestedQuery()
45+
{
46+
$q = '
47+
query NestedQuery {
48+
hero {
49+
name
50+
friends {
51+
name
52+
appearsIn
53+
friends {
54+
name
55+
}
56+
}
57+
}
58+
}
59+
';
60+
GraphQL::execute(
61+
StarWarsSchema::build(),
62+
$q
63+
);
64+
}
65+
66+
public function benchQueryWithFragment()
67+
{
68+
$q = '
69+
query UseFragment {
70+
luke: human(id: "1000") {
71+
...HumanFragment
72+
}
73+
leia: human(id: "1003") {
74+
...HumanFragment
75+
}
76+
}
77+
78+
fragment HumanFragment on Human {
79+
name
80+
homePlanet
81+
}
82+
';
83+
84+
GraphQL::execute(
85+
StarWarsSchema::build(),
86+
$q
87+
);
88+
}
89+
90+
public function benchStarWarsIntrospectionQuery()
91+
{
92+
GraphQL::execute(
93+
StarWarsSchema::build(),
94+
$this->introQuery
95+
);
96+
}
97+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
namespace GraphQL\Benchmarks\Utils;
3+
4+
use GraphQL\Language\AST\DocumentNode;
5+
use GraphQL\Language\AST\FieldNode;
6+
use GraphQL\Language\AST\NameNode;
7+
use GraphQL\Language\AST\OperationDefinitionNode;
8+
use GraphQL\Language\AST\SelectionSetNode;
9+
use GraphQL\Language\Printer;
10+
use GraphQL\Schema;
11+
use GraphQL\Type\Definition\FieldDefinition;
12+
use GraphQL\Type\Definition\InterfaceType;
13+
use GraphQL\Type\Definition\ObjectType;
14+
use GraphQL\Type\Definition\WrappingType;
15+
use GraphQL\Utils;
16+
17+
class QueryGenerator
18+
{
19+
private $schema;
20+
21+
private $maxLeafFields;
22+
23+
private $currentLeafFields;
24+
25+
public function __construct(Schema $schema, $percentOfLeafFields)
26+
{
27+
$this->schema = $schema;
28+
29+
Utils::invariant(0 < $percentOfLeafFields && $percentOfLeafFields <= 1);
30+
31+
$totalFields = 0;
32+
foreach ($schema->getTypeMap() as $type) {
33+
if ($type instanceof ObjectType) {
34+
$totalFields += count($type->getFields());
35+
}
36+
}
37+
38+
$this->maxLeafFields = max(1, round($totalFields * $percentOfLeafFields));
39+
$this->currentLeafFields = 0;
40+
}
41+
42+
public function buildQuery()
43+
{
44+
$qtype = $this->schema->getQueryType();
45+
46+
$ast = new DocumentNode([
47+
'definitions' => [
48+
new OperationDefinitionNode([
49+
'name' => new NameNode(['value' => 'TestQuery']),
50+
'operation' => 'query',
51+
'selectionSet' => $this->buildSelectionSet($qtype->getFields())
52+
])
53+
]
54+
]);
55+
56+
return Printer::doPrint($ast);
57+
}
58+
59+
/**
60+
* @param FieldDefinition[] $fields
61+
* @return SelectionSetNode
62+
*/
63+
public function buildSelectionSet($fields)
64+
{
65+
$selections[] = new FieldNode([
66+
'name' => new NameNode(['value' => '__typename'])
67+
]);
68+
$this->currentLeafFields++;
69+
70+
foreach ($fields as $field) {
71+
if ($this->currentLeafFields >= $this->maxLeafFields) {
72+
break;
73+
}
74+
75+
$type = $field->getType();
76+
77+
if ($type instanceof WrappingType) {
78+
$type = $type->getWrappedType(true);
79+
}
80+
81+
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
82+
$selectionSet = $this->buildSelectionSet($type->getFields());
83+
} else {
84+
$selectionSet = null;
85+
$this->currentLeafFields++;
86+
}
87+
88+
$selections[] = new FieldNode([
89+
'name' => new NameNode(['value' => $field->name]),
90+
'selectionSet' => $selectionSet
91+
]);
92+
}
93+
94+
$selectionSet = new SelectionSetNode([
95+
'selections' => $selections
96+
]);
97+
98+
return $selectionSet;
99+
}
100+
}

0 commit comments

Comments
 (0)