File: selection_bench_test.go

package info (click to toggle)
golang-github-graph-gophers-graphql-go 1.7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,452 kB
  • sloc: sh: 373; javascript: 21; makefile: 5
file content (117 lines) | stat: -rw-r--r-- 3,470 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package graphql_test

import (
	"context"
	"fmt"
	"testing"

	graphql "github.com/graph-gophers/graphql-go"
)

// This benchmark compares query execution when resolvers do NOT call the
// selection helpers vs when they call SelectedFieldNames at object boundaries.
// It documents the lazy overhead of computing child field selections.

const lazyBenchSchema = `
			schema { query: Query }
			type Query { hero: Human }
			type Human { id: ID! name: String friends: [Human!]! }
		`

// Simple in-memory data graph.
type human struct {
	id, name string
	friends  []*human
}

// Build a small graph once outside the benchmark loops.
var benchHero *human

func init() {
	// Create 5 friends (no recursive friends to keep size stable).
	friends := make([]*human, 5)
	for i := range friends {
		friends[i] = &human{id: fmt.Sprintf("F%d", i), name: "Friend"}
	}
	benchHero = &human{id: "H1", name: "Hero", friends: friends}
}

// Baseline resolvers (do NOT invoke selection helpers).
type (
	rootBaseline          struct{}
	humanResolverBaseline struct{ h *human }
)

func (r *rootBaseline) Hero(ctx context.Context) *humanResolverBaseline {
	return &humanResolverBaseline{h: benchHero}
}
func (h *humanResolverBaseline) ID() graphql.ID { return graphql.ID(h.h.id) }
func (h *humanResolverBaseline) Name() *string  { return &h.h.name }
func (h *humanResolverBaseline) Friends(ctx context.Context) []*humanResolverBaseline {
	out := make([]*humanResolverBaseline, len(h.h.friends))
	for i, f := range h.h.friends {
		out[i] = &humanResolverBaseline{h: f}
	}
	return out
}

// Instrumented resolvers (CALL selection helpers once per object-level resolver).
type (
	rootWithSel          struct{}
	humanResolverWithSel struct{ h *human }
)

func (r *rootWithSel) Hero(ctx context.Context) *humanResolverWithSel {
	// Selection list for hero object (id, name, friends)
	_ = graphql.SelectedFieldNames(ctx)
	return &humanResolverWithSel{h: benchHero}
}

func (h *humanResolverWithSel) ID(ctx context.Context) graphql.ID { // leaf: expecting empty slice
	return graphql.ID(h.h.id)
}

func (h *humanResolverWithSel) Name(ctx context.Context) *string { // leaf
	return &h.h.name
}

func (h *humanResolverWithSel) Friends(ctx context.Context) []*humanResolverWithSel {
	// Selection list on list field: children of Human inside list items.
	_ = graphql.SelectedFieldNames(ctx)
	out := make([]*humanResolverWithSel, len(h.h.friends))
	for i, f := range h.h.friends {
		// For each friend object we also call once at the object resolver boundary.
		out[i] = &humanResolverWithSel{h: f}
	}
	return out
}

// Query used for both benchmarks.
const lazyBenchQuery = `query { hero { id name friends { id name } } }`

func BenchmarkFieldSelections_NoUsage(b *testing.B) {
	schema := graphql.MustParseSchema(lazyBenchSchema, &rootBaseline{})
	ctx := context.Background()
	b.ReportAllocs()
	for b.Loop() {
		_ = schema.Exec(ctx, lazyBenchQuery, "", nil)
	}
}

func BenchmarkFieldSelections_Disabled_NoUsage(b *testing.B) {
	schema := graphql.MustParseSchema(lazyBenchSchema, &rootBaseline{}, graphql.DisableFieldSelections())
	ctx := context.Background()
	b.ReportAllocs()
	for b.Loop() {
		_ = schema.Exec(ctx, lazyBenchQuery, "", nil)
	}
}

func BenchmarkFieldSelections_WithSelectedFieldNames(b *testing.B) {
	schema := graphql.MustParseSchema(lazyBenchSchema, &rootWithSel{})
	ctx := context.Background()
	b.ReportAllocs()
	for b.Loop() {
		_ = schema.Exec(ctx, lazyBenchQuery, "", nil)
	}
}