Skip to content

Commit

Permalink
Merge pull request #268 from mrhenry/benchmarks
Browse files Browse the repository at this point in the history
Add benchmarks
  • Loading branch information
chris-ramon authored Jan 7, 2018
2 parents 363d9c3 + 4c66ed6 commit 5e7dd4f
Show file tree
Hide file tree
Showing 4 changed files with 379 additions and 1 deletion.
110 changes: 110 additions & 0 deletions benchutil/list_schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package benchutil

import (
"fmt"

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

type color struct {
Hex string
R int
G int
B int
}

func ListSchemaWithXItems(x int) graphql.Schema {

list := generateXListItems(x)

color := graphql.NewObject(graphql.ObjectConfig{
Name: "Color",
Description: "A color",
Fields: graphql.Fields{
"hex": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
Description: "Hex color code.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if c, ok := p.Source.(color); ok {
return c.Hex, nil
}
return nil, nil
},
},
"r": &graphql.Field{
Type: graphql.NewNonNull(graphql.Int),
Description: "Red value.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if c, ok := p.Source.(color); ok {
return c.R, nil
}
return nil, nil
},
},
"g": &graphql.Field{
Type: graphql.NewNonNull(graphql.Int),
Description: "Green value.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if c, ok := p.Source.(color); ok {
return c.G, nil
}
return nil, nil
},
},
"b": &graphql.Field{
Type: graphql.NewNonNull(graphql.Int),
Description: "Blue value.",
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
if c, ok := p.Source.(color); ok {
return c.B, nil
}
return nil, nil
},
},
},
})

queryType := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"colors": {
Type: graphql.NewList(color),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return list, nil
},
},
},
})

colorSchema, _ := graphql.NewSchema(graphql.SchemaConfig{
Query: queryType,
})

return colorSchema
}

var colors []color

func init() {
colors = make([]color, 0, 256*16*16)

for r := 0; r < 256; r++ {
for g := 0; g < 16; g++ {
for b := 0; b < 16; b++ {
colors = append(colors, color{
Hex: fmt.Sprintf("#%x%x%x", r, g, b),
R: r,
G: g,
B: b,
})
}
}
}
}

func generateXListItems(x int) []color {
if x > len(colors) {
x = len(colors)
}
return colors[0:x]
}
144 changes: 144 additions & 0 deletions benchutil/wide_schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package benchutil

import (
"fmt"

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

func WideSchemaWithXFieldsAndYItems(x int, y int) graphql.Schema {
wide := graphql.NewObject(graphql.ObjectConfig{
Name: "Wide",
Description: "An object",
Fields: generateXWideFields(x),
})

queryType := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"wide": {
Type: graphql.NewList(wide),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
out := make([]struct{}, 0, y)
for i := 0; i < y; i++ {
out = append(out, struct{}{})
}
return out, nil
},
},
},
})

wideSchema, _ := graphql.NewSchema(graphql.SchemaConfig{
Query: queryType,
})

return wideSchema
}

func generateXWideFields(x int) graphql.Fields {
fields := graphql.Fields{}
for i := 0; i < x; i++ {
fields[generateFieldNameFromX(i)] = generateWideFieldFromX(i)
}
return fields
}

func generateWideFieldFromX(x int) *graphql.Field {
return &graphql.Field{
Type: generateWideTypeFromX(x),
Resolve: generateWideResolveFromX(x),
}
}

func generateWideTypeFromX(x int) graphql.Type {
switch x % 8 {
case 0:
return graphql.String
case 1:
return graphql.NewNonNull(graphql.String)
case 2:
return graphql.Int
case 3:
return graphql.NewNonNull(graphql.Int)
case 4:
return graphql.Float
case 5:
return graphql.NewNonNull(graphql.Float)
case 6:
return graphql.Boolean
case 7:
return graphql.NewNonNull(graphql.Boolean)
}

return nil
}

func generateFieldNameFromX(x int) string {
var out string
alphabet := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "z"}
v := x
for {
r := v % 10
out = alphabet[r] + out
v = v / 10
if v == 0 {
break
}
}
return out
}

func generateWideResolveFromX(x int) func(p graphql.ResolveParams) (interface{}, error) {
switch x % 8 {
case 0:
return func(p graphql.ResolveParams) (interface{}, error) {
return fmt.Sprint(x), nil
}
case 1:
return func(p graphql.ResolveParams) (interface{}, error) {
return fmt.Sprint(x), nil
}
case 2:
return func(p graphql.ResolveParams) (interface{}, error) {
return x, nil
}
case 3:
return func(p graphql.ResolveParams) (interface{}, error) {
return x, nil
}
case 4:
return func(p graphql.ResolveParams) (interface{}, error) {
return float64(x), nil
}
case 5:
return func(p graphql.ResolveParams) (interface{}, error) {
return float64(x), nil
}
case 6:
return func(p graphql.ResolveParams) (interface{}, error) {
if x%2 == 0 {
return false, nil
}
return true, nil
}
case 7:
return func(p graphql.ResolveParams) (interface{}, error) {
if x%2 == 0 {
return false, nil
}
return true, nil
}
}

return nil
}

func WideSchemaQuery(x int) string {
var fields string
for i := 0; i < x; i++ {
fields = fields + generateFieldNameFromX(i) + " "
}

return fmt.Sprintf("query { wide { %s} }", fields)
}
124 changes: 124 additions & 0 deletions graphql_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package graphql_test

import (
"testing"

"github.com/graphql-go/graphql"
"github.com/graphql-go/graphql/benchutil"
)

type B struct {
Query string
Schema graphql.Schema
}

func benchGraphql(bench B, p graphql.Params, t testing.TB) {
result := graphql.Do(p)
if len(result.Errors) > 0 {
t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
}
}

// Benchmark a reasonably large list of small items.
func BenchmarkListQuery_1(b *testing.B) {
nItemsListQueryBenchmark(1)(b)
}

func BenchmarkListQuery_100(b *testing.B) {
nItemsListQueryBenchmark(100)(b)
}

func BenchmarkListQuery_1K(b *testing.B) {
nItemsListQueryBenchmark(1000)(b)
}

func BenchmarkListQuery_10K(b *testing.B) {
nItemsListQueryBenchmark(10 * 1000)(b)
}

func BenchmarkListQuery_100K(b *testing.B) {
nItemsListQueryBenchmark(100 * 1000)(b)
}

func nItemsListQueryBenchmark(x int) func(b *testing.B) {
return func(b *testing.B) {
schema := benchutil.ListSchemaWithXItems(x)

bench := B{
Query: `
query {
colors {
hex
r
g
b
}
}
`,
Schema: schema,
}

for i := 0; i < b.N; i++ {

params := graphql.Params{
Schema: schema,
RequestString: bench.Query,
}
benchGraphql(bench, params, b)
}
}
}

func BenchmarkWideQuery_1_1(b *testing.B) {
nFieldsyItemsQueryBenchmark(1, 1)(b)
}

func BenchmarkWideQuery_10_1(b *testing.B) {
nFieldsyItemsQueryBenchmark(10, 1)(b)
}

func BenchmarkWideQuery_100_1(b *testing.B) {
nFieldsyItemsQueryBenchmark(100, 1)(b)
}

func BenchmarkWideQuery_1K_1(b *testing.B) {
nFieldsyItemsQueryBenchmark(1000, 1)(b)
}

func BenchmarkWideQuery_1_10(b *testing.B) {
nFieldsyItemsQueryBenchmark(1, 10)(b)
}

func BenchmarkWideQuery_10_10(b *testing.B) {
nFieldsyItemsQueryBenchmark(10, 10)(b)
}

func BenchmarkWideQuery_100_10(b *testing.B) {
nFieldsyItemsQueryBenchmark(100, 10)(b)
}

func BenchmarkWideQuery_1K_10(b *testing.B) {
nFieldsyItemsQueryBenchmark(1000, 10)(b)
}

func nFieldsyItemsQueryBenchmark(x int, y int) func(b *testing.B) {
return func(b *testing.B) {
schema := benchutil.WideSchemaWithXFieldsAndYItems(x, y)
query := benchutil.WideSchemaQuery(x)

bench := B{
Query: query,
Schema: schema,
}

b.ResetTimer()

for i := 0; i < b.N; i++ {
params := graphql.Params{
Schema: schema,
RequestString: bench.Query,
}
benchGraphql(bench, params, b)
}
}
}
2 changes: 1 addition & 1 deletion testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ func init() {
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
id, err := strconv.Atoi(p.Args["id"].(string))
if err != nil {
return nil, err
return nil, err
}
return GetHuman(id), nil
},
Expand Down

0 comments on commit 5e7dd4f

Please sign in to comment.