Skip to content

Commit 3e004e9

Browse files
committed
ExecutionStepInfo now has a direct transform without a Builder
1 parent bfd567e commit 3e004e9

File tree

4 files changed

+193
-54
lines changed

4 files changed

+193
-54
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package graphql.execution;
2+
3+
import graphql.Scalars;
4+
import graphql.language.Field;
5+
import org.openjdk.jmh.annotations.Benchmark;
6+
import org.openjdk.jmh.annotations.BenchmarkMode;
7+
import org.openjdk.jmh.annotations.Fork;
8+
import org.openjdk.jmh.annotations.Level;
9+
import org.openjdk.jmh.annotations.Measurement;
10+
import org.openjdk.jmh.annotations.Mode;
11+
import org.openjdk.jmh.annotations.OutputTimeUnit;
12+
import org.openjdk.jmh.annotations.Param;
13+
import org.openjdk.jmh.annotations.Scope;
14+
import org.openjdk.jmh.annotations.Setup;
15+
import org.openjdk.jmh.annotations.State;
16+
import org.openjdk.jmh.annotations.TearDown;
17+
import org.openjdk.jmh.annotations.Warmup;
18+
import org.openjdk.jmh.infra.Blackhole;
19+
import org.openjdk.jmh.profile.GCProfiler;
20+
import org.openjdk.jmh.runner.Runner;
21+
import org.openjdk.jmh.runner.options.Options;
22+
import org.openjdk.jmh.runner.options.OptionsBuilder;
23+
24+
import java.util.concurrent.TimeUnit;
25+
26+
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
27+
28+
@State(Scope.Benchmark)
29+
@Warmup(iterations = 2, time = 5)
30+
@Measurement(iterations = 2)
31+
@Fork(2)
32+
public class ExecutionStepInfoBenchmark {
33+
@Param({"1000000", "2000000"})
34+
int howManyItems = 1000000;
35+
36+
@Setup(Level.Trial)
37+
public void setUp() {
38+
}
39+
40+
@TearDown(Level.Trial)
41+
public void tearDown() {
42+
}
43+
44+
45+
MergedField mergedField = MergedField.newMergedField().addField(Field.newField("f").build()).build();
46+
47+
ResultPath path = ResultPath.rootPath().segment("f");
48+
ExecutionStepInfo rootStepInfo = newExecutionStepInfo()
49+
.path(path).type(Scalars.GraphQLString)
50+
.field(mergedField)
51+
.build();
52+
53+
54+
@Benchmark
55+
@BenchmarkMode(Mode.Throughput)
56+
@OutputTimeUnit(TimeUnit.SECONDS)
57+
public void benchMarkDirectConstructorThroughput(Blackhole blackhole) {
58+
for (int i = 0; i < howManyItems; i++) {
59+
ResultPath newPath = path.segment(1);
60+
ExecutionStepInfo newOne = rootStepInfo.transform(Scalars.GraphQLInt, rootStepInfo, newPath);
61+
blackhole.consume(newOne);
62+
}
63+
}
64+
65+
@Benchmark
66+
@BenchmarkMode(Mode.Throughput)
67+
@OutputTimeUnit(TimeUnit.SECONDS)
68+
public void benchMarkBuilderThroughput(Blackhole blackhole) {
69+
for (int i = 0; i < howManyItems; i++) {
70+
ResultPath newPath = path.segment(1);
71+
ExecutionStepInfo newOne = newExecutionStepInfo(rootStepInfo).parentInfo(rootStepInfo)
72+
.type(Scalars.GraphQLInt).path(newPath).build();
73+
blackhole.consume(newOne);
74+
}
75+
}
76+
77+
public static void main(String[] args) throws Exception {
78+
Options opt = new OptionsBuilder()
79+
.include("graphql.execution.ExecutionStepInfoBenchmark")
80+
.addProfiler(GCProfiler.class)
81+
.build();
82+
83+
new Runner(opt).run();
84+
}
85+
86+
}

src/main/java/graphql/execution/ExecutionStepInfo.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package graphql.execution;
22

3+
import graphql.Internal;
34
import graphql.PublicApi;
45
import graphql.collect.ImmutableMapWithNullValues;
56
import graphql.schema.GraphQLFieldDefinition;
@@ -77,6 +78,19 @@ private ExecutionStepInfo(Builder builder) {
7778
this.fieldContainer = builder.fieldContainer;
7879
}
7980

81+
/*
82+
* This constructor allows for a slightly ( 1% ish) faster transformation without an intermediate Builder object
83+
*/
84+
private ExecutionStepInfo(GraphQLOutputType type, ResultPath path, ExecutionStepInfo parent, MergedField field, GraphQLFieldDefinition fieldDefinition, GraphQLObjectType fieldContainer, Supplier<ImmutableMapWithNullValues<String, Object>> arguments) {
85+
this.type = assertNotNull(type, () -> "you must provide a graphql type");
86+
this.path = path;
87+
this.parent = parent;
88+
this.field = field;
89+
this.fieldDefinition = fieldDefinition;
90+
this.fieldContainer = fieldContainer;
91+
this.arguments = arguments;
92+
}
93+
8094
/**
8195
* The GraphQLObjectType where fieldDefinition is defined.
8296
* Note:
@@ -193,13 +207,12 @@ public boolean hasParent() {
193207
public ExecutionStepInfo changeTypeWithPreservedNonNull(GraphQLOutputType newType) {
194208
assertTrue(!GraphQLTypeUtil.isNonNull(newType), () -> "newType can't be non null");
195209
if (isNonNullType()) {
196-
return newExecutionStepInfo(this).type(GraphQLNonNull.nonNull(newType)).build();
210+
return transform(GraphQLNonNull.nonNull(newType));
197211
} else {
198-
return newExecutionStepInfo(this).type(newType).build();
212+
return transform(newType);
199213
}
200214
}
201215

202-
203216
/**
204217
* @return the type in graphql SDL format, eg [typeName!]!
205218
*/
@@ -216,6 +229,16 @@ public String toString() {
216229
'}';
217230
}
218231

232+
@Internal
233+
ExecutionStepInfo transform(GraphQLOutputType type) {
234+
return new ExecutionStepInfo(type, path, parent, field, fieldDefinition, fieldContainer, arguments);
235+
}
236+
237+
@Internal
238+
ExecutionStepInfo transform(GraphQLOutputType type, ExecutionStepInfo parent, ResultPath path) {
239+
return new ExecutionStepInfo(type, path, parent, field, fieldDefinition, fieldContainer, arguments);
240+
}
241+
219242
public ExecutionStepInfo transform(Consumer<Builder> builderConsumer) {
220243
Builder builder = new Builder(this);
221244
builderConsumer.accept(builder);
Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,92 @@
11
package graphql.execution;
22

33
import graphql.Internal;
4+
import graphql.collect.ImmutableMapWithNullValues;
5+
import graphql.language.Argument;
6+
import graphql.schema.GraphQLArgument;
7+
import graphql.schema.GraphQLCodeRegistry;
8+
import graphql.schema.GraphQLFieldDefinition;
49
import graphql.schema.GraphQLList;
10+
import graphql.schema.GraphQLObjectType;
511
import graphql.schema.GraphQLOutputType;
12+
import graphql.util.FpKit;
13+
import org.jspecify.annotations.NonNull;
14+
import org.jspecify.annotations.NullMarked;
15+
import org.jspecify.annotations.Nullable;
16+
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.function.Supplier;
20+
21+
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
622

723
@Internal
24+
@NullMarked
825
public class ExecutionStepInfoFactory {
926

1027
public ExecutionStepInfo newExecutionStepInfoForListElement(ExecutionStepInfo executionInfo, ResultPath indexedPath) {
1128
GraphQLList fieldType = (GraphQLList) executionInfo.getUnwrappedNonNullType();
1229
GraphQLOutputType typeInList = (GraphQLOutputType) fieldType.getWrappedType();
13-
return executionInfo.transform(builder -> builder
14-
.parentInfo(executionInfo)
15-
.type(typeInList)
16-
.path(indexedPath));
30+
return executionInfo.transform(typeInList, executionInfo, indexedPath);
31+
}
32+
33+
/**
34+
* Builds the type info hierarchy for the current field
35+
*
36+
* @param executionContext the execution context in play
37+
* @param parameters contains the parameters holding the fields to be executed and source object
38+
* @param fieldDefinition the field definition to build type info for
39+
* @param fieldContainer the field container
40+
*
41+
* @return a new type info
42+
*/
43+
public ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionContext,
44+
ExecutionStrategyParameters parameters,
45+
GraphQLFieldDefinition fieldDefinition,
46+
@Nullable GraphQLObjectType fieldContainer) {
47+
MergedField field = parameters.getField();
48+
ExecutionStepInfo parentStepInfo = parameters.getExecutionStepInfo();
49+
GraphQLOutputType fieldType = fieldDefinition.getType();
50+
List<GraphQLArgument> fieldArgDefs = fieldDefinition.getArguments();
51+
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues = ImmutableMapWithNullValues::emptyMap;
52+
//
53+
// no need to create args at all if there are none on the field def
54+
//
55+
if (!fieldArgDefs.isEmpty()) {
56+
argumentValues = getArgumentValues(executionContext, fieldArgDefs, field.getArguments());
57+
}
58+
59+
60+
return newExecutionStepInfo()
61+
.type(fieldType)
62+
.fieldDefinition(fieldDefinition)
63+
.fieldContainer(fieldContainer)
64+
.field(field)
65+
.path(parameters.getPath())
66+
.parentInfo(parentStepInfo)
67+
.arguments(argumentValues)
68+
.build();
1769
}
1870

71+
@NonNull
72+
private static Supplier<ImmutableMapWithNullValues<String, Object>> getArgumentValues(ExecutionContext executionContext,
73+
List<GraphQLArgument> fieldArgDefs,
74+
List<Argument> fieldArgs) {
75+
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues;
76+
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
77+
Supplier<ImmutableMapWithNullValues<String, Object>> argValuesSupplier = () -> {
78+
Map<String, Object> resolvedValues = ValuesResolver.getArgumentValues(codeRegistry,
79+
fieldArgDefs,
80+
fieldArgs,
81+
executionContext.getCoercedVariables(),
82+
executionContext.getGraphQLContext(),
83+
executionContext.getLocale());
84+
85+
return ImmutableMapWithNullValues.copyOf(resolvedValues);
86+
};
87+
argumentValues = FpKit.intraThreadMemoize(argValuesSupplier);
88+
return argumentValues;
89+
}
90+
91+
1992
}

src/main/java/graphql/execution/ExecutionStrategy.java

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import graphql.TrivialDataFetcher;
1515
import graphql.TypeMismatchError;
1616
import graphql.UnresolvedTypeError;
17-
import graphql.collect.ImmutableMapWithNullValues;
1817
import graphql.execution.directives.QueryDirectives;
1918
import graphql.execution.directives.QueryDirectivesImpl;
2019
import graphql.execution.incremental.DeferredExecutionSupport;
@@ -29,7 +28,6 @@
2928
import graphql.execution.reactive.ReactiveSupport;
3029
import graphql.extensions.ExtensionsBuilder;
3130
import graphql.introspection.Introspection;
32-
import graphql.language.Argument;
3331
import graphql.language.Field;
3432
import graphql.normalized.ExecutableNormalizedField;
3533
import graphql.normalized.ExecutableNormalizedOperation;
@@ -38,12 +36,10 @@
3836
import graphql.schema.DataFetchingEnvironment;
3937
import graphql.schema.DataFetchingFieldSelectionSet;
4038
import graphql.schema.DataFetchingFieldSelectionSetImpl;
41-
import graphql.schema.GraphQLArgument;
4239
import graphql.schema.GraphQLCodeRegistry;
4340
import graphql.schema.GraphQLEnumType;
4441
import graphql.schema.GraphQLFieldDefinition;
4542
import graphql.schema.GraphQLObjectType;
46-
import graphql.schema.GraphQLOutputType;
4743
import graphql.schema.GraphQLScalarType;
4844
import graphql.schema.GraphQLSchema;
4945
import graphql.schema.GraphQLType;
@@ -64,7 +60,6 @@
6460
import java.util.function.Supplier;
6561

6662
import static graphql.execution.Async.exceptionallyCompletedFuture;
67-
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
6863
import static graphql.execution.FieldCollectorParameters.newParameters;
6964
import static graphql.execution.FieldValueInfo.CompleteValueType.ENUM;
7065
import static graphql.execution.FieldValueInfo.CompleteValueType.LIST;
@@ -1091,48 +1086,10 @@ protected ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionCo
10911086
ExecutionStrategyParameters parameters,
10921087
GraphQLFieldDefinition fieldDefinition,
10931088
GraphQLObjectType fieldContainer) {
1094-
MergedField field = parameters.getField();
1095-
ExecutionStepInfo parentStepInfo = parameters.getExecutionStepInfo();
1096-
GraphQLOutputType fieldType = fieldDefinition.getType();
1097-
List<GraphQLArgument> fieldArgDefs = fieldDefinition.getArguments();
1098-
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues = ImmutableMapWithNullValues::emptyMap;
1099-
//
1100-
// no need to create args at all if there are none on the field def
1101-
//
1102-
if (!fieldArgDefs.isEmpty()) {
1103-
argumentValues = getArgumentValues(executionContext, fieldArgDefs, field.getArguments());
1104-
}
1105-
1106-
1107-
return newExecutionStepInfo()
1108-
.type(fieldType)
1109-
.fieldDefinition(fieldDefinition)
1110-
.fieldContainer(fieldContainer)
1111-
.field(field)
1112-
.path(parameters.getPath())
1113-
.parentInfo(parentStepInfo)
1114-
.arguments(argumentValues)
1115-
.build();
1116-
}
1117-
1118-
@NonNull
1119-
private static Supplier<ImmutableMapWithNullValues<String, Object>> getArgumentValues(ExecutionContext executionContext,
1120-
List<GraphQLArgument> fieldArgDefs,
1121-
List<Argument> fieldArgs) {
1122-
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues;
1123-
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
1124-
Supplier<ImmutableMapWithNullValues<String, Object>> argValuesSupplier = () -> {
1125-
Map<String, Object> resolvedValues = ValuesResolver.getArgumentValues(codeRegistry,
1126-
fieldArgDefs,
1127-
fieldArgs,
1128-
executionContext.getCoercedVariables(),
1129-
executionContext.getGraphQLContext(),
1130-
executionContext.getLocale());
1131-
1132-
return ImmutableMapWithNullValues.copyOf(resolvedValues);
1133-
};
1134-
argumentValues = FpKit.intraThreadMemoize(argValuesSupplier);
1135-
return argumentValues;
1089+
return executionStepInfoFactory.createExecutionStepInfo(executionContext,
1090+
parameters,
1091+
fieldDefinition,
1092+
fieldContainer);
11361093
}
11371094

11381095
// Errors that result from the execution of deferred fields are kept in the deferred context only.

0 commit comments

Comments
 (0)