Skip to content

Commit

Permalink
Merge pull request #18 from jensneuse/finalize-v1
Browse files Browse the repository at this point in the history
Finalize v1
  • Loading branch information
jensneuse authored Feb 20, 2019
2 parents 1e620a7 + 295ff41 commit 85a5af4
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 39 deletions.
46 changes: 20 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@ Currently implemented:

- lexing
- parsing

TODO (1.0 planned @ 02/2019):

- [x] cleanup different styles of testing
- [ ] validation (WIP)
- [ ] introspection
- [ ] schema printer
- validation

## Usage

Expand All @@ -35,29 +29,29 @@ See pkg/parser/parser_test.go

```
pkg: github.com/jensneuse/graphql-go-tools/pkg/parser
BenchmarkParser-4 100000 19724 ns/op 0 B/op 0 allocs/op
BenchmarkParser-4 100000 19548 ns/op 0 B/op 0 allocs/op
BenchmarkParser-4 100000 19409 ns/op 0 B/op 0 allocs/op
BenchmarkParser-4 100000 19233 ns/op 0 B/op 0 allocs/op
BenchmarkParser-4 50000 29490 ns/op 0 B/op 0 allocs/op
BenchmarkParser-4 50000 29931 ns/op 0 B/op 0 allocs/op
BenchmarkParser-4 50000 28779 ns/op 1 B/op 0 allocs/op
BenchmarkParser-4 50000 29176 ns/op 0 B/op 0 allocs/op
```

In a previous release I found that nested slice structs accounted for huge amounts of gc and decreased performance.
This is fixed. I've also added resource pooling to avoid slice grows. As a caveat the parser is not thread safe.
A possible solution would be to have a pool of parsers which should work fine as parser doesn't allocate a lot of memory.
```
pkg: github.com/jensneuse/graphql-go-tools/pkg/validator
BenchmarkValidator/test_valid_schema-4 200000 7823 ns/op 0 B/op 0 allocs/op
BenchmarkValidator/test_valid_schema-4 200000 7836 ns/op 0 B/op 0 allocs/op
BenchmarkValidator/test_valid_schema-4 200000 7766 ns/op 0 B/op 0 allocs/op
BenchmarkValidator/test_valid_schema-4 200000 7777 ns/op 0 B/op 0 allocs/op
BenchmarkValidator/introspection_query-4 3000 407511 ns/op 44 B/op 0 allocs/op
BenchmarkValidator/introspection_query-4 3000 410118 ns/op 44 B/op 0 allocs/op
BenchmarkValidator/introspection_query-4 3000 405893 ns/op 45 B/op 0 allocs/op
BenchmarkValidator/introspection_query-4 3000 403380 ns/op 56 B/op 0 allocs/op
```

Other than that I don't see any value in further optimizing for performance as it is "good enough".
To put these numbers into perspective. Parsing + validating the (quite complex) introspection query is < 0.5ms (on my 2013 MacBook) which should be acceptable for web applications.

For comparison (using the exact same input & hardware):
It's important to note that gc is kept at a minimum which should enable applications built on top of this library to have almost zero deviation regarding latency.

```
goos: darwin
goarch: amd64
pkg: github.com/vektah/gqlparser/parser
BenchmarkParser-4 50000 36128 ns/op 16112 B/op 217 allocs/op
BenchmarkParser-4 50000 35946 ns/op 16112 B/op 217 allocs/op
BenchmarkParser-4 50000 36039 ns/op 16112 B/op 217 allocs/op
BenchmarkParser-4 50000 35985 ns/op 16112 B/op 217 allocs/op
```
You'll probably add bottlenecks at another layer, e.g. invoking a database.

## Contributors

Expand All @@ -67,7 +61,7 @@ This repository was initially developed and maintained by one single person:
These users are actively maintaining and/or developing as of today:

- [Jens Neuse][jens-neuse-github] (Project Lead)
- [Jonas Bergner][jonas-bergner-github] (Major contributions to the parser, extensive testing)
- [Jonas Bergner][jonas-bergner-github] (Contributions to the initial version of the parser, contributions to the tests)

[jens-neuse-github]: https://github.com/jensneuse
[jonas-bergner-github]: https://github.com/java-jonas
Expand Down
14 changes: 7 additions & 7 deletions pkg/lookup/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ func (r *refPool) get() []int {
return r.refs[r.position][:0]
}

func New(p *parser.Parser) *Lookup {
func New(p *parser.Parser, refPoolSize int) *Lookup {

var pool refPool
pool.position = -1
pool.refs = make([][]int, 128)
pool.refs = make([][]int, refPoolSize)

for i := 0; i < 128; i++ {
for i := 0; i < refPoolSize; i++ {
pool.refs[i] = make([]int, 8)
}

Expand Down Expand Up @@ -205,10 +205,10 @@ func (l *Lookup) Type(i int) document.Type {
return l.p.ParsedDefinitions.Types[i]
}

func (l *Lookup) ObjectTypeDefinitionByName(name int) (document.ObjectTypeDefinition, bool) {
for _, definition := range l.p.ParsedDefinitions.ObjectTypeDefinitions {
if name == definition.Name {
return definition, true
func (l *Lookup) ObjectTypeDefinitionByName(name int) (definition document.ObjectTypeDefinition, exists bool) {
for i := range l.p.ParsedDefinitions.ObjectTypeDefinitions {
if name == l.p.ParsedDefinitions.ObjectTypeDefinitions[i].Name {
return l.p.ParsedDefinitions.ObjectTypeDefinitions[i], true
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/lookup/lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestLookup(t *testing.T) {
panic(err)
}

l := New(p)
l := New(p, 256)

err = p.ParseExecutableDefinition([]byte(input))
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,11 @@ func NewParser(withOptions ...Option) *Parser {
Arguments: make(document.Arguments, 0, options.minimumSliceSize),
ArgumentSets: make([]document.ArgumentSet, 0, options.minimumSliceSize),
Directives: make(document.Directives, 0, options.minimumSliceSize),
DirectiveSets: make([]document.DirectiveSet, 0, options.minimumSliceSize),
DirectiveSets: make([]document.DirectiveSet, 0, options.minimumSliceSize*2),
EnumTypeDefinitions: make(document.EnumTypeDefinitions, 0, options.minimumSliceSize),
EnumValuesDefinitions: make(document.EnumValueDefinitions, 0, options.minimumSliceSize),
ArgumentsDefinitions: make(document.ArgumentsDefinitions, 0, options.minimumSliceSize),
FieldDefinitions: make(document.FieldDefinitions, 0, options.minimumSliceSize),
FieldDefinitions: make(document.FieldDefinitions, 0, options.minimumSliceSize*2),
InputValueDefinitions: make(document.InputValueDefinitions, 0, options.minimumSliceSize),
InputObjectTypeDefinitions: make(document.InputObjectTypeDefinitions, 0, options.minimumSliceSize),
DirectiveDefinitions: make(document.DirectiveDefinitions, 0, options.minimumSliceSize),
Expand Down
4 changes: 2 additions & 2 deletions pkg/validation/rules/execution/execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestExecutionValidation(t *testing.T) {
panic(err)
}

l := lookup.New(p)
l := lookup.New(p, 256)
l.ResetPool()

err = p.ParseExecutableDefinition([]byte(input))
Expand Down Expand Up @@ -2535,7 +2535,7 @@ func BenchmarkExecutionValidation(t *testing.B) {
panic(err)
}

l := lookup.New(p)
l := lookup.New(p, 256)

err = p.ParseExecutableDefinition([]byte(input))
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var (

func (v *Validator) SetInput(p *parser.Parser) {
if v.l == nil {
v.l = lookup.New(p)
v.l = lookup.New(p, 256)
} else {
v.l.SetParser(p)
}
Expand Down

0 comments on commit 85a5af4

Please sign in to comment.