Reduce allocations in Async$Many and ResultPath on execution hot path by andimarek · Pull Request #4252 · graphql-java/graphql-java

@andimarek @claude

In the all-synchronous execution path (no CompletableFutures), Async$Many
allocated an Object[] to collect field values, then copied them into a new
ArrayList in materialisedList(). Replace the copy with Arrays.asList() which
wraps the existing array at zero cost.

Benchmarked with a new ExecutionBenchmark (balanced tree: ~530 fields, ~2000
result scalars, depth 5) showing ~5% throughput improvement on the synchronous
path. Also adds async-profiler support to build.gradle for JMH profiling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@andimarek @claude

…cations

The toString representation of ResultPath was eagerly computed in the
constructor via initString(), but is never read during normal query
execution — only used for error reporting. Make it lazy (computed on
first toString() call) to eliminate all string work from the hot path.

Also inline segmentToString() into initString() to avoid intermediate
String allocations when the value is eventually computed, letting Java's
StringConcatFactory handle it as a single multi-arg concat.

Benchmarked ~30-78% throughput improvement vs master across all execution
benchmarks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@andimarek changed the title Avoid redundant array copy in Async$Many materialisedList Reduce allocations in Async$Many and ResultPath on execution hot path

Feb 19, 2026
Every field fetch created a throwaway FieldCoordinates object just for a
HashMap lookup. Add an internal nested Map<String, Map<String, ...>>
(typeName → fieldName → factory) built at CodeRegistry construction time,
and an internal getDataFetcher(String, String, GraphQLFieldDefinition)
method that does the lookup by strings directly. Use this in
ExecutionStrategy.fetchField to skip FieldCoordinates creation entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@andimarek

Remove @internal from the String-based getDataFetcher overload and add
proper javadoc documenting the ~54 KB/op allocation savings and 5-9%
throughput improvement over the FieldCoordinates-based lookup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…init

Since toStringValue is lazily computed (once per path), the manual
inlining of segmentToString() provides no measurable performance
benefit. Simplify back to the clean delegation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>