Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/jmh/java/graphql/execution/ExecutionStepInfoBenchmark.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package graphql.execution;

import graphql.Scalars;
import graphql.language.Field;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;

@State(Scope.Benchmark)
@Warmup(iterations = 2, time = 5)
@Measurement(iterations = 2)
@Fork(2)
public class ExecutionStepInfoBenchmark {
@Param({"1000000", "2000000"})
int howManyItems = 1000000;

@Setup(Level.Trial)
public void setUp() {
}

@TearDown(Level.Trial)
public void tearDown() {
}


MergedField mergedField = MergedField.newMergedField().addField(Field.newField("f").build()).build();

ResultPath path = ResultPath.rootPath().segment("f");
ExecutionStepInfo rootStepInfo = newExecutionStepInfo()
.path(path).type(Scalars.GraphQLString)
.field(mergedField)
.build();


@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public void benchMarkDirectConstructorThroughput(Blackhole blackhole) {
for (int i = 0; i < howManyItems; i++) {
ResultPath newPath = path.segment(1);
ExecutionStepInfo newOne = rootStepInfo.transform(Scalars.GraphQLInt, rootStepInfo, newPath);
blackhole.consume(newOne);
}
}

@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public void benchMarkBuilderThroughput(Blackhole blackhole) {
for (int i = 0; i < howManyItems; i++) {
ResultPath newPath = path.segment(1);
ExecutionStepInfo newOne = newExecutionStepInfo(rootStepInfo).parentInfo(rootStepInfo)
.type(Scalars.GraphQLInt).path(newPath).build();
blackhole.consume(newOne);
}
}

public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include("graphql.execution.ExecutionStepInfoBenchmark")
.addProfiler(GCProfiler.class)
.build();

new Runner(opt).run();
}

}
35 changes: 32 additions & 3 deletions src/main/java/graphql/execution/ExecutionStepInfo.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package graphql.execution;

import graphql.Internal;
import graphql.PublicApi;
import graphql.collect.ImmutableMapWithNullValues;
import graphql.schema.GraphQLFieldDefinition;
Expand Down Expand Up @@ -77,6 +78,25 @@ private ExecutionStepInfo(Builder builder) {
this.fieldContainer = builder.fieldContainer;
}

/*
* This constructor allows for a slightly ( 1% ish) faster transformation without an intermediate Builder object
*/
private ExecutionStepInfo(GraphQLOutputType type,
ResultPath path,
ExecutionStepInfo parent,
MergedField field,
GraphQLFieldDefinition fieldDefinition,
GraphQLObjectType fieldContainer,
Supplier<ImmutableMapWithNullValues<String, Object>> arguments) {
this.type = assertNotNull(type, () -> "you must provide a graphql type");
this.path = path;
this.parent = parent;
this.field = field;
this.fieldDefinition = fieldDefinition;
this.fieldContainer = fieldContainer;
this.arguments = arguments;
}

/**
* The GraphQLObjectType where fieldDefinition is defined.
* Note:
Expand Down Expand Up @@ -193,13 +213,12 @@ public boolean hasParent() {
public ExecutionStepInfo changeTypeWithPreservedNonNull(GraphQLOutputType newType) {
assertTrue(!GraphQLTypeUtil.isNonNull(newType), () -> "newType can't be non null");
if (isNonNullType()) {
return newExecutionStepInfo(this).type(GraphQLNonNull.nonNull(newType)).build();
return transform(GraphQLNonNull.nonNull(newType));
} else {
return newExecutionStepInfo(this).type(newType).build();
return transform(newType);
}
}


/**
* @return the type in graphql SDL format, eg [typeName!]!
*/
Expand All @@ -216,6 +235,16 @@ public String toString() {
'}';
}

@Internal
ExecutionStepInfo transform(GraphQLOutputType type) {
return new ExecutionStepInfo(type, path, parent, field, fieldDefinition, fieldContainer, arguments);
}

@Internal
ExecutionStepInfo transform(GraphQLOutputType type, ExecutionStepInfo parent, ResultPath path) {
return new ExecutionStepInfo(type, path, parent, field, fieldDefinition, fieldContainer, arguments);
}

public ExecutionStepInfo transform(Consumer<Builder> builderConsumer) {
Builder builder = new Builder(this);
builderConsumer.accept(builder);
Expand Down
81 changes: 77 additions & 4 deletions src/main/java/graphql/execution/ExecutionStepInfoFactory.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,92 @@
package graphql.execution;

import graphql.Internal;
import graphql.collect.ImmutableMapWithNullValues;
import graphql.language.Argument;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLOutputType;
import graphql.util.FpKit;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;

@Internal
@NullMarked
public class ExecutionStepInfoFactory {

public ExecutionStepInfo newExecutionStepInfoForListElement(ExecutionStepInfo executionInfo, ResultPath indexedPath) {
GraphQLList fieldType = (GraphQLList) executionInfo.getUnwrappedNonNullType();
GraphQLOutputType typeInList = (GraphQLOutputType) fieldType.getWrappedType();
return executionInfo.transform(builder -> builder
.parentInfo(executionInfo)
.type(typeInList)
.path(indexedPath));
return executionInfo.transform(typeInList, executionInfo, indexedPath);
}

/**
* Builds the type info hierarchy for the current field
*
* @param executionContext the execution context in play
* @param parameters contains the parameters holding the fields to be executed and source object
* @param fieldDefinition the field definition to build type info for
* @param fieldContainer the field container
*
* @return a new type info
*/
public ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
GraphQLFieldDefinition fieldDefinition,
@Nullable GraphQLObjectType fieldContainer) {
MergedField field = parameters.getField();
ExecutionStepInfo parentStepInfo = parameters.getExecutionStepInfo();
GraphQLOutputType fieldType = fieldDefinition.getType();
List<GraphQLArgument> fieldArgDefs = fieldDefinition.getArguments();
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues = ImmutableMapWithNullValues::emptyMap;
//
// no need to create args at all if there are none on the field def
//
if (!fieldArgDefs.isEmpty()) {
argumentValues = getArgumentValues(executionContext, fieldArgDefs, field.getArguments());
}


return newExecutionStepInfo()
.type(fieldType)
.fieldDefinition(fieldDefinition)
.fieldContainer(fieldContainer)
.field(field)
.path(parameters.getPath())
.parentInfo(parentStepInfo)
.arguments(argumentValues)
.build();
}

@NonNull
private static Supplier<ImmutableMapWithNullValues<String, Object>> getArgumentValues(ExecutionContext executionContext,
List<GraphQLArgument> fieldArgDefs,
List<Argument> fieldArgs) {
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues;
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
Supplier<ImmutableMapWithNullValues<String, Object>> argValuesSupplier = () -> {
Map<String, Object> resolvedValues = ValuesResolver.getArgumentValues(codeRegistry,
fieldArgDefs,
fieldArgs,
executionContext.getCoercedVariables(),
executionContext.getGraphQLContext(),
executionContext.getLocale());

return ImmutableMapWithNullValues.copyOf(resolvedValues);
};
argumentValues = FpKit.intraThreadMemoize(argValuesSupplier);
return argumentValues;
}


}
51 changes: 4 additions & 47 deletions src/main/java/graphql/execution/ExecutionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import graphql.TrivialDataFetcher;
import graphql.TypeMismatchError;
import graphql.UnresolvedTypeError;
import graphql.collect.ImmutableMapWithNullValues;
import graphql.execution.directives.QueryDirectives;
import graphql.execution.directives.QueryDirectivesImpl;
import graphql.execution.incremental.DeferredExecutionSupport;
Expand All @@ -29,7 +28,6 @@
import graphql.execution.reactive.ReactiveSupport;
import graphql.extensions.ExtensionsBuilder;
import graphql.introspection.Introspection;
import graphql.language.Argument;
import graphql.language.Field;
import graphql.normalized.ExecutableNormalizedField;
import graphql.normalized.ExecutableNormalizedOperation;
Expand All @@ -38,12 +36,10 @@
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.DataFetchingFieldSelectionSet;
import graphql.schema.DataFetchingFieldSelectionSetImpl;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLScalarType;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLType;
Expand All @@ -64,7 +60,6 @@
import java.util.function.Supplier;

import static graphql.execution.Async.exceptionallyCompletedFuture;
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
import static graphql.execution.FieldCollectorParameters.newParameters;
import static graphql.execution.FieldValueInfo.CompleteValueType.ENUM;
import static graphql.execution.FieldValueInfo.CompleteValueType.LIST;
Expand Down Expand Up @@ -1091,48 +1086,10 @@ protected ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionCo
ExecutionStrategyParameters parameters,
GraphQLFieldDefinition fieldDefinition,
GraphQLObjectType fieldContainer) {
MergedField field = parameters.getField();
ExecutionStepInfo parentStepInfo = parameters.getExecutionStepInfo();
GraphQLOutputType fieldType = fieldDefinition.getType();
List<GraphQLArgument> fieldArgDefs = fieldDefinition.getArguments();
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues = ImmutableMapWithNullValues::emptyMap;
//
// no need to create args at all if there are none on the field def
//
if (!fieldArgDefs.isEmpty()) {
argumentValues = getArgumentValues(executionContext, fieldArgDefs, field.getArguments());
}


return newExecutionStepInfo()
.type(fieldType)
.fieldDefinition(fieldDefinition)
.fieldContainer(fieldContainer)
.field(field)
.path(parameters.getPath())
.parentInfo(parentStepInfo)
.arguments(argumentValues)
.build();
}

@NonNull
private static Supplier<ImmutableMapWithNullValues<String, Object>> getArgumentValues(ExecutionContext executionContext,
List<GraphQLArgument> fieldArgDefs,
List<Argument> fieldArgs) {
Supplier<ImmutableMapWithNullValues<String, Object>> argumentValues;
GraphQLCodeRegistry codeRegistry = executionContext.getGraphQLSchema().getCodeRegistry();
Supplier<ImmutableMapWithNullValues<String, Object>> argValuesSupplier = () -> {
Map<String, Object> resolvedValues = ValuesResolver.getArgumentValues(codeRegistry,
fieldArgDefs,
fieldArgs,
executionContext.getCoercedVariables(),
executionContext.getGraphQLContext(),
executionContext.getLocale());

return ImmutableMapWithNullValues.copyOf(resolvedValues);
};
argumentValues = FpKit.intraThreadMemoize(argValuesSupplier);
return argumentValues;
return executionStepInfoFactory.createExecutionStepInfo(executionContext,
parameters,
fieldDefinition,
fieldContainer);
}

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