Skip to content

Commit

Permalink
Merge pull request #68 from goccy/feature/improve-performance
Browse files Browse the repository at this point in the history
Improve performance ( Phase 1 )
  • Loading branch information
goccy authored Dec 29, 2019
2 parents 65f21cf + af4f3ec commit 009880f
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 67 deletions.
52 changes: 52 additions & 0 deletions benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package yaml_test

import (
"testing"

"github.com/goccy/go-yaml"
goyaml2 "gopkg.in/yaml.v2"
goyaml3 "gopkg.in/yaml.v3"
)

func Benchmark(b *testing.B) {
const src = `---
id: 1
message: Hello, World
verified: true
elements:
- one
- 0.02
- null
- -inf
`
type T struct {
ID int `yaml:"id"`
Message string `yaml:"message"`
Verified bool `yaml:"verified,omitempty"`
}

b.Run("gopkg.in/yaml.v2", func(b *testing.B) {
var t T
for i := 0; i < b.N; i++ {
if err := goyaml2.Unmarshal([]byte(src), &t); err != nil {
b.Fatal(err)
}
}
})
b.Run("gopkg.in/yaml.v3", func(b *testing.B) {
var t T
for i := 0; i < b.N; i++ {
if err := goyaml3.Unmarshal([]byte(src), &t); err != nil {
b.Fatal(err)
}
}
})
b.Run("github.com/goccy/go-yaml", func(b *testing.B) {
var t T
for i := 0; i < b.N; i++ {
if err := yaml.Unmarshal([]byte(src), &t); err != nil {
b.Fatal(err)
}
}
})
}
50 changes: 31 additions & 19 deletions decode.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package yaml

import (
"bytes"
"encoding/base64"
"fmt"
"io"
Expand Down Expand Up @@ -82,6 +83,22 @@ func (d *Decoder) castToFloat(v interface{}) interface{} {
return 0
}

func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) {
switch n := node.(type) {
case *ast.MappingValueNode:
if n.Key.Type() == ast.MergeKeyType {
d.setToMapValue(n.Value, m)
} else {
key := n.Key.GetToken().Value
m[key] = d.nodeToValue(n.Value)
}
case *ast.MappingNode:
for _, value := range n.Values {
d.setToMapValue(value, m)
}
}
}

func (d *Decoder) nodeToValue(node ast.Node) interface{} {
switch n := node.(type) {
case *ast.NullNode:
Expand Down Expand Up @@ -122,28 +139,23 @@ func (d *Decoder) nodeToValue(node ast.Node) interface{} {
case *ast.LiteralNode:
return n.Value.GetValue()
case *ast.MappingValueNode:
m := map[string]interface{}{}
if n.Key.Type() == ast.MergeKeyType {
mapValue := d.nodeToValue(n.Value).(map[string]interface{})
for k, v := range mapValue {
m[k] = v
}
} else {
key := n.Key.GetToken().Value
m[key] = d.nodeToValue(n.Value)
m := map[string]interface{}{}
d.setToMapValue(n.Value, m)
return m
}
key := n.Key.GetToken().Value
return map[string]interface{}{
key: d.nodeToValue(n.Value),
}
return m
case *ast.MappingNode:
m := map[string]interface{}{}
m := make(map[string]interface{}, len(n.Values))
for _, value := range n.Values {
subMap := d.nodeToValue(value).(map[string]interface{})
for k, v := range subMap {
m[k] = v
}
d.setToMapValue(value, m)
}
return m
case *ast.SequenceNode:
v := []interface{}{}
v := make([]interface{}, 0, len(n.Values))
for _, value := range n.Values {
v = append(v, d.nodeToValue(value))
}
Expand Down Expand Up @@ -946,11 +958,11 @@ func (d *Decoder) Decode(v interface{}) error {
if rv.Type().Kind() != reflect.Ptr {
return errors.ErrDecodeRequiredPointerType
}
bytes, err := ioutil.ReadAll(d.reader)
if err != nil {
return errors.Wrapf(err, "failed to read buffer")
var buf bytes.Buffer
if _, err := io.Copy(&buf, d.reader); err != nil {
return errors.Wrapf(err, "failed to copy from reader")
}
node, err := d.decode(bytes)
node, err := d.decode(buf.Bytes())
if err != nil {
return errors.Wrapf(err, "failed to decode")
}
Expand Down
92 changes: 65 additions & 27 deletions scanner/context.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,68 @@
package scanner

import (
"strings"
"sync"

"github.com/goccy/go-yaml/token"
)

// Context context at scanning
type Context struct {
idx int
size int
src []rune
buf []rune
obuf []rune
tokens token.Tokens
isRawFolded bool
isLiteral bool
isFolded bool
isSingleLine bool
literalOpt string
}
idx int
size int
notSpaceCharPos int
notSpaceOrgCharPos int
src []rune
buf []rune
obuf []rune
tokens token.Tokens
isRawFolded bool
isLiteral bool
isFolded bool
isSingleLine bool
literalOpt string
}

var (
ctxPool = sync.Pool{
New: func() interface{} {
return createContext()
},
}
)

func newContext(src []rune) *Context {
func createContext() *Context {
return &Context{
idx: 0,
size: len(src),
src: src,
tokens: token.Tokens{},
buf: make([]rune, 0, len(src)),
obuf: make([]rune, 0, len(src)),
isSingleLine: true,
}
}

func newContext(src []rune) *Context {
ctx := ctxPool.Get().(*Context)
ctx.reset(src)
return ctx
}

func (c *Context) release() {
ctxPool.Put(c)
}

func (c *Context) reset(src []rune) {
c.idx = 0
c.size = len(src)
c.src = src
c.tokens = c.tokens[:0]
c.resetBuffer()
c.isSingleLine = true
}

func (c *Context) resetBuffer() {
c.buf = c.buf[:0]
c.obuf = c.obuf[:0]
c.notSpaceCharPos = 0
c.notSpaceOrgCharPos = 0
}

func (c *Context) isSaveIndentMode() bool {
Expand All @@ -57,20 +84,29 @@ func (c *Context) addToken(tk *token.Token) {
}

func (c *Context) addBuf(r rune) {
if len(c.buf) == 0 && r == ' ' {
return
}
c.buf = append(c.buf, r)
if r != ' ' {
c.notSpaceCharPos = len(c.buf)
}
}

func (c *Context) addOriginBuf(r rune) {
c.obuf = append(c.obuf, r)
if r != ' ' {
c.notSpaceOrgCharPos = len(c.obuf)
}
}

func (c *Context) removeRightSpaceFromBuf() int {
trimmedBuf := strings.TrimRight(string(c.obuf), " ")
buflen := len([]rune(trimmedBuf))
trimmedBuf := c.obuf[:c.notSpaceOrgCharPos]
buflen := len(trimmedBuf)
diff := len(c.obuf) - buflen
if diff > 0 {
c.obuf = c.obuf[:buflen]
c.buf = []rune(c.bufferedSrc())
c.buf = c.bufferedSrc()
}
return diff
}
Expand Down Expand Up @@ -133,9 +169,12 @@ func (c *Context) nextPos() int {
return c.idx + 1
}

func (c *Context) bufferedSrc() string {
src := strings.TrimLeft(string(c.buf), " ")
src = strings.TrimRight(src, " ")
func (c *Context) existsBuffer() bool {
return len(c.bufferedSrc()) != 0
}

func (c *Context) bufferedSrc() []rune {
src := c.buf[:c.notSpaceCharPos]
if len(src) > 0 && src[len(src)-1] == '\n' && c.isDocument() && c.literalOpt == "-" {
// remove end '\n' character
src = src[:len(src)-1]
Expand All @@ -151,8 +190,7 @@ func (c *Context) bufferedToken(pos *token.Position) *token.Token {
if len(source) == 0 {
return nil
}
tk := token.New(source, string(c.obuf), pos)
c.buf = c.buf[:0]
c.obuf = c.obuf[:0]
tk := token.New(string(source), string(c.obuf), pos)
c.resetBuffer()
return tk
}
Loading

0 comments on commit 009880f

Please sign in to comment.