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
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using HotChocolate.Language;

namespace HotChocolate.Language.Benchmarks;

[MemoryDiagnoser]
[ShortRunJob(RuntimeMoniker.Net10_0)]
public class ParserBenchmarks
{
private byte[] _kitchenSinkQuery = null!;
private byte[] _schemaKitchenSink = null!;
private byte[] _simpleQuery = null!;

[GlobalSetup]
public void Setup()
{
_kitchenSinkQuery = Encoding.UTF8.GetBytes("""
"Query description"
query queryName("$foo description" $foo: ComplexType, "$site description" $site: Site = MOBILE) {
whoever123is: node(id: [123, 456]) {
id ,
... on User @defer {
field2 {
id ,
alias: field1(first:10, after:$foo,) @include(if: $foo) {
id,
...frag
}
}
}
... @skip(unless: $foo) {
id
}
... {
id
}
}
}

"Mutation description"
mutation likeStory {
like(story: 123) @defer {
story {
id
}
}
}

"Subscription description"
subscription StoryLikeSubscription("$input description" $input: StoryLikeSubscribeInput) {
storyLikeSubscribe(input: $input) {
story {
likers {
count
}
likeSentence {
text
}
}
}
}

"Fragment description"
fragment frag on Friend {
foo(size: $size, bar: $b, obj: {key: "value"})
}

{
unnamed(truthy: true, falsey: false, nullish: null),
query
}
""");

_schemaKitchenSink = Encoding.UTF8.GetBytes("""
schema {
query: QueryType
mutation: MutationType
}

type Foo implements Bar & Baz {
one: Type
two(argument: InputType!): Type
three(argument: InputType, other: String): Int
four(argument: String = "string"): String
five(argument: [String] = ["string", "string"]): String
six(argument: InputType = {key: "value"}): Type
seven(argument: Int = null): Type
}

type AnnotatedObject @onObject(arg: "value") {
annotatedField(arg: Type = "default" @onArg): Type @onField
}

interface Bar {
one: Type
four(argument: String = "string"): String
}

union Feed = Story | Article | Advert

scalar CustomScalar

enum Site {
DESKTOP
MOBILE
}

input InputType {
key: String!
answer: Int = 42
}

directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

extend type Foo {
seven(argument: [String]): Type
}

extend interface Bar {
two(argument: InputType!): Type
}
""");

_simpleQuery = Encoding.UTF8.GetBytes("""
{
hero {
name
friends {
name
}
}
}
""");
}

[Benchmark]
public DocumentNode ParseKitchenSinkQuery()
=> Utf8GraphQLParser.Parse(_kitchenSinkQuery);

[Benchmark]
public DocumentNode ParseSchemaKitchenSink()
=> Utf8GraphQLParser.Parse(_schemaKitchenSink);

[Benchmark]
public DocumentNode ParseSimpleQuery()
=> Utf8GraphQLParser.Parse(_simpleQuery);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using HotChocolate.Language;

namespace HotChocolate.Language.Benchmarks;

[MemoryDiagnoser]
[ShortRunJob(RuntimeMoniker.Net10_0)]
public class UnescapeBenchmarks
{
private byte[] _noEscapes = null!;
private byte[] _singleEscape = null!;
private byte[] _multipleEscapes = null!;
private byte[] _unicodeEscapes = null!;
private byte[] _blockString = null!;

[GlobalSetup]
public void Setup()
{
_noEscapes = Encoding.UTF8.GetBytes("This is a simple string with no escape characters at all and it is moderately long");
_singleEscape = Encoding.UTF8.GetBytes("This is a string with a single\\nnewline escape in it somewhere");
_multipleEscapes = Encoding.UTF8.GetBytes("Line1\\nLine2\\tTabbed\\rReturn\\\\Backslash\\\"Quote\\/Slash");
_unicodeEscapes = Encoding.UTF8.GetBytes("Unicode: \\u0041\\u0042\\u0043 and \\u00e9\\u00e8\\u00ea more text");
_blockString = Encoding.UTF8.GetBytes("This is a block string\n with indentation\n and multiple lines\n but no escapes needed");
}

[Benchmark]
public int UnescapeNoEscapes()
{
ReadOnlySpan<byte> input = _noEscapes;
Span<byte> output = stackalloc byte[_noEscapes.Length];
Utf8Helper.Unescape(in input, ref output, isBlockString: false);
return output.Length;
}

[Benchmark]
public int UnescapeSingleEscape()
{
ReadOnlySpan<byte> input = _singleEscape;
Span<byte> output = stackalloc byte[_singleEscape.Length];
Utf8Helper.Unescape(in input, ref output, isBlockString: false);
return output.Length;
}

[Benchmark]
public int UnescapeMultipleEscapes()
{
ReadOnlySpan<byte> input = _multipleEscapes;
Span<byte> output = stackalloc byte[_multipleEscapes.Length];
Utf8Helper.Unescape(in input, ref output, isBlockString: false);
return output.Length;
}

[Benchmark]
public int UnescapeUnicodeEscapes()
{
ReadOnlySpan<byte> input = _unicodeEscapes;
Span<byte> output = stackalloc byte[_unicodeEscapes.Length];
Utf8Helper.Unescape(in input, ref output, isBlockString: false);
return output.Length;
}

[Benchmark]
public int UnescapeBlockString()
{
ReadOnlySpan<byte> input = _blockString;
Span<byte> output = stackalloc byte[_blockString.Length];
Utf8Helper.Unescape(in input, ref output, isBlockString: true);
return output.Length;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<InternalsVisibleTo Include="HotChocolate.Types" />
<InternalsVisibleTo Include="HotChocolate.Fusion.Execution" />
<InternalsVisibleTo Include="HotChocolate.Utilities.Introspection" />
<InternalsVisibleTo Include="HotChocolate.Language.Benchmarks" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,52 @@ private ITypeSystemExtensionNode ParseTypeExtension()

MoveNext();

if (_reader.Kind == TokenKind.Name)
if (_reader.Kind == TokenKind.Name && _reader.Value.Length > 0)
{
if (_reader.Value.SequenceEqual(GraphQLKeywords.Schema))
switch (_reader.Value[0])
{
return ParseSchemaExtension(in start);
}

if (_reader.Value.SequenceEqual(GraphQLKeywords.Scalar))
{
return ParseScalarTypeExtension(in start);
}

if (_reader.Value.SequenceEqual(GraphQLKeywords.Type))
{
return ParseObjectTypeExtension(in start);
}

if (_reader.Value.SequenceEqual(GraphQLKeywords.Interface))
{
return ParseInterfaceTypeExtension(in start);
}

if (_reader.Value.SequenceEqual(GraphQLKeywords.Union))
{
return ParseUnionTypeExtension(in start);
}

if (_reader.Value.SequenceEqual(GraphQLKeywords.Enum))
{
return ParseEnumTypeExtension(in start);
}

if (_reader.Value.SequenceEqual(GraphQLKeywords.Input))
{
return ParseInputObjectTypeExtension(in start);
case (byte)'s':
if (_reader.Value.SequenceEqual(GraphQLKeywords.Schema))
{
return ParseSchemaExtension(in start);
}
if (_reader.Value.SequenceEqual(GraphQLKeywords.Scalar))
{
return ParseScalarTypeExtension(in start);
}
break;

case (byte)'t':
if (_reader.Value.SequenceEqual(GraphQLKeywords.Type))
{
return ParseObjectTypeExtension(in start);
}
break;

case (byte)'i':
if (_reader.Value.SequenceEqual(GraphQLKeywords.Interface))
{
return ParseInterfaceTypeExtension(in start);
}
if (_reader.Value.SequenceEqual(GraphQLKeywords.Input))
{
return ParseInputObjectTypeExtension(in start);
}
break;

case (byte)'u':
if (_reader.Value.SequenceEqual(GraphQLKeywords.Union))
{
return ParseUnionTypeExtension(in start);
}
break;

case (byte)'e':
if (_reader.Value.SequenceEqual(GraphQLKeywords.Enum))
{
return ParseEnumTypeExtension(in start);
}
break;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,33 @@ private OperationDefinitionNode ParseOperationDefinition()
selectionSet);
}

/// <summary>
/// Parses an operation definition with a pre-matched operation type,
/// avoiding redundant keyword comparison.
/// </summary>
private OperationDefinitionNode ParseOperationDefinition(OperationType operation)
{
var start = Start();

// skip the operation type keyword (already matched by caller)
MoveNext();

var name = _reader.Kind == TokenKind.Name ? ParseName() : null;
var variableDefinitions = ParseVariableDefinitions();
var directives = ParseDirectives(false);
var selectionSet = ParseSelectionSet();
var location = CreateLocation(in start);

return new OperationDefinitionNode(
location,
name,
TakeDescription(),
operation,
variableDefinitions,
directives,
selectionSet);
}

/// <summary>
/// Parses a shorthand form operation definition.
/// <see cref="OperationDefinitionNode" />:
Expand Down Expand Up @@ -179,7 +206,7 @@ private SelectionSetNode ParseSelectionSet()
TokenPrinter.Print(ref _reader)));
}

var selections = new List<ISelectionNode>();
var selections = new List<ISelectionNode>(8);

// skip opening token
MoveNext();
Expand Down Expand Up @@ -269,7 +296,7 @@ private List<ArgumentNode> ParseArguments(bool isConstant)
{
if (_reader.Kind == TokenKind.LeftParenthesis)
{
var list = new List<ArgumentNode>();
var list = new List<ArgumentNode>(4);

// skip opening token
MoveNext();
Expand Down
Loading
Loading