Skip to content

Commit

Permalink
benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
AsterDY committed Jan 16, 2024
1 parent 0415087 commit 45f273e
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 40 deletions.
10 changes: 9 additions & 1 deletion ast/raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sort"
"strconv"

"github.com/bytedance/sonic/encoder"
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
)
Expand All @@ -16,14 +17,21 @@ type Value struct {
js string
}

func NewValue(json string) Value {
func NewValueJSON(json string) Value {
p := NewParser(json)
s, e := p.skip()
if e != 0 {
return errRawNode(p.ExportError(e))
}
return value(json[s:p.p])
}
func NewValue(val interface{}) Value {
js, err := encoder.Encode(val, 0)
if err != nil {
return errRawNode(err)
}
return value(rt.Mem2Str(js))
}

func value(js string) Value {
return Value{
Expand Down
58 changes: 29 additions & 29 deletions ast/raw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestValAPI(t *testing.T) {
}
for i, c := range cases {
fmt.Println(i, c)
node := NewValue(c.js)
node := NewValueJSON(c.js)
rt := reflect.ValueOf(&node)
m := rt.MethodByName(c.method)
rets := m.Call([]reflect.Value{})
Expand Down Expand Up @@ -71,7 +71,7 @@ func TestGetMany(t *testing.T) {
}
for i, c := range cases {
fmt.Println(i, c)
node := NewValue(c.js)
node := NewValueJSON(c.js)
var err error
if kvs, ok := c.kvs.([]keyVal); ok {
keys := []string{}
Expand Down Expand Up @@ -129,7 +129,7 @@ func TestSetMany(t *testing.T) {

for i, c := range cases {
println(i, c.name)
node := NewValue(c.js)
node := NewValueJSON(c.js)
var err error
if kvs, ok := c.kvs.([]keyVal); ok {
keys := []string{}
Expand Down Expand Up @@ -284,18 +284,18 @@ func TestRawNode_Set(t *testing.T) {
err string
out string
}{
{"exist object",`{"a":1}`,"a",NewValue(`2`),true,"",`{"a":2}`},
{"not-exist object space",`{"b":1}`,"a",NewValue(`2`),false,"",`{"b":1,"a":2}`},
{"not-exist object",`{"b":1}`,"a",NewValue(`2`),false,"",`{"b":1,"a":2}`},
{"empty object",`{}`,"a",NewValue(`2`),false,"",`{"a":2}`},
{"exist array",`[1]`,0,NewValue(`2`),true,"",`[2]`},
{"not exist array",`[1]`,1,NewValue(`2`),false,"",`[1,2]`},
{"not exist array over",`[1]`,99,NewValue(`2`),false,"",`[1,2]`},
{"empty array",`[]`,1,NewValue(`2`),false,"",`[2]`},
{"exist object",`{"a":1}`,"a",NewValueJSON(`2`),true,"",`{"a":2}`},
{"not-exist object space",`{"b":1}`,"a",NewValueJSON(`2`),false,"",`{"b":1,"a":2}`},
{"not-exist object",`{"b":1}`,"a",NewValueJSON(`2`),false,"",`{"b":1,"a":2}`},
{"empty object",`{}`,"a",NewValueJSON(`2`),false,"",`{"a":2}`},
{"exist array",`[1]`,0,NewValueJSON(`2`),true,"",`[2]`},
{"not exist array",`[1]`,1,NewValueJSON(`2`),false,"",`[1,2]`},
{"not exist array over",`[1]`,99,NewValueJSON(`2`),false,"",`[1,2]`},
{"empty array",`[]`,1,NewValueJSON(`2`),false,"",`[2]`},
}
for _, c := range tests {
println(c.name)
root := NewValue(c.js)
root := NewValueJSON(c.js)
var exist bool
var err error
if key, ok:= c.key.(string); ok{
Expand Down Expand Up @@ -327,21 +327,21 @@ func TestRawNode_SetByPath(t *testing.T) {
err string
out string
}{
{"exist object",`{"a":1}`,[]interface{}{"a"},NewValue(`2`),true,"",`{"a":2}`},
{"not-exist object",`{"b":1}`,[]interface{}{"a"},NewValue(`2`),false,"",`{"b":1,"a":2}`},
{"empty object",`{}`,[]interface{}{"a"},NewValue(`2`),false,"",`{"a":2}`},
{"empty object 2",`{}`,[]interface{}{"a",1},NewValue(`2`),false,"",`{"a":[2]}`},
{"empty object 3",`{}`,[]interface{}{"a",1,"a"},NewValue(`2`),false,"",`{"a":[{"a":2}]}`},
{"exist array",`[1]`,[]interface{}{0},NewValue(`2`),true,"",`[2]`},
{"not exist array",`[1]`,[]interface{}{1},NewValue(`2`),false,"",`[1,2]`},
{"empty array",`[]`,[]interface{}{1},NewValue(`2`),false,"",`[2]`},
{"empty array 2",`[]`,[]interface{}{1,1},NewValue(`2`),false,"",`[[2]]`},
{"empty array 3",`[]`,[]interface{}{1,"a",1},NewValue(`2`),false,"",`[{"a":[2]}]`},
{"empty array 3",`[]`,[]interface{}{1,"a","a"},NewValue(`2`),false,"",`[{"a":{"a":2}}]`},
{"exist object",`{"a":1}`,[]interface{}{"a"},NewValueJSON(`2`),true,"",`{"a":2}`},
{"not-exist object",`{"b":1}`,[]interface{}{"a"},NewValueJSON(`2`),false,"",`{"b":1,"a":2}`},
{"empty object",`{}`,[]interface{}{"a"},NewValueJSON(`2`),false,"",`{"a":2}`},
{"empty object 2",`{}`,[]interface{}{"a",1},NewValueJSON(`2`),false,"",`{"a":[2]}`},
{"empty object 3",`{}`,[]interface{}{"a",1,"a"},NewValueJSON(`2`),false,"",`{"a":[{"a":2}]}`},
{"exist array",`[1]`,[]interface{}{0},NewValueJSON(`2`),true,"",`[2]`},
{"not exist array",`[1]`,[]interface{}{1},NewValueJSON(`2`),false,"",`[1,2]`},
{"empty array",`[]`,[]interface{}{1},NewValueJSON(`2`),false,"",`[2]`},
{"empty array 2",`[]`,[]interface{}{1,1},NewValueJSON(`2`),false,"",`[[2]]`},
{"empty array 3",`[]`,[]interface{}{1,"a",1},NewValueJSON(`2`),false,"",`[{"a":[2]}]`},
{"empty array 3",`[]`,[]interface{}{1,"a","a"},NewValueJSON(`2`),false,"",`[{"a":{"a":2}}]`},
}
for _, c := range tests {
println(c.name)
root := NewValue(c.js)
root := NewValueJSON(c.js)
exist, err := root.SetByPath(c.val, c.paths...)
if err != nil && err.Error() != c.err {
t.Fatal(err)
Expand Down Expand Up @@ -383,7 +383,7 @@ func TestRawNode_UnsetByPath(t *testing.T) {
}
for _, c := range tests {
println(c.name)
root := NewValue(c.js)
root := NewValueJSON(c.js)
exist, err := root.UnsetByPath(c.paths...)
if err != nil && err.Error() != c.err {
t.Fatal(err)
Expand Down Expand Up @@ -420,7 +420,7 @@ func TestRawNode_Unset(t *testing.T) {

for _, c := range tests {
println(c.name)
root := NewValue(c.js)
root := NewValueJSON(c.js)
var exist bool
var err error
if key, ok:= c.key.(string); ok{
Expand Down Expand Up @@ -476,7 +476,7 @@ func TestRawNode_UnsetMany(t *testing.T) {

for _, c := range tests {
println(c.name)
root := NewValue(c.js)
root := NewValueJSON(c.js)
var err error
if keys, ok := c.key.([]string); ok{
err = root.UnsetMany(keys)
Expand Down Expand Up @@ -505,7 +505,7 @@ func BenchmarkGetByPath_ReuseNode(b *testing.B) {
}
})
b.Run("Value", func(b *testing.B) {
cont := NewValue(_TwitterJson)
cont := NewValueJSON(_TwitterJson)
b.ResetTimer()
for i:=0; i<b.N; i++ {
_, _ = cont.GetByPath("statuses", 3, "entities", "hashtags", 0, "text").String()
Expand All @@ -524,7 +524,7 @@ func BenchmarkNodesGetByPath_NewNode(b *testing.B) {
b.Run("Value", func(b *testing.B) {
b.ResetTimer()
for i:=0; i<b.N; i++ {
cont := NewValue(_TwitterJson)
cont := NewValueJSON(_TwitterJson)
_, _ = cont.GetByPath("statuses", 3, "entities", "hashtags", 0, "text").String()
}
})
Expand Down
36 changes: 35 additions & 1 deletion ast/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"

"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
)

type Searcher struct {
Expand Down Expand Up @@ -60,7 +61,7 @@ func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
}

// GetValueByPath
func (self Searcher) GetValueByPath(path ...interface{}) (Value, error) {
func (self *Searcher) GetValueByPath(path ...interface{}) (Value, error) {
if self.parser.s == "" {
err := errors.New("empty input")
return errRawNode(err), err
Expand All @@ -79,4 +80,37 @@ func (self Searcher) GetValueByPath(path ...interface{}) (Value, error) {
return errRawNode(e), e
}
return Value{int(t), self.parser.s[s:self.parser.p]}, nil
}

// GetValueByPath
func (self *Searcher) SetValueByPath(val Value, path ...interface{}) (string, error) {
if self.parser.s == "" {
err := errors.New("empty input")
return self.parser.s, err
}

self.parser.p = 0
s, err := self.parser.getByPath(path...)

if err != 0 {
if err != _ERR_NOT_FOUND {
e := self.parser.ExportError(err)
return self.parser.s, e
} else {
// not exist, slow path
n := value(self.parser.s)
if _, err := n.SetByPath(val, path...); err != nil {
return self.parser.s, err
}
return n.js, nil
}
}

// exist, fast-path replace
e := self.parser.p
b := make([]byte, 0, len(self.parser.s)+len(val.js)-(e-s))
b = append(b, self.parser.s[:s]...)
b = append(b, val.js...)
b = append(b, self.parser.s[e:]...)
return rt.Mem2Str(b), nil
}

Check failure on line 116 in ast/search.go

View workflow job for this annotation

GitHub Actions / build (1.20.x)

syntax error: unexpected var after top level declaration
115 changes: 106 additions & 9 deletions external_jsonlib_test/benchmark_test/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
package benchmark_test

import (
`fmt`
`math`
`testing`

`github.com/bytedance/sonic/ast`
`github.com/buger/jsonparser`
jsoniter `github.com/json-iterator/go`
`github.com/tidwall/gjson`
`github.com/tidwall/sjson`
"fmt"
"math"
"testing"

"github.com/buger/jsonparser"
"github.com/bytedance/sonic/ast"
jsoniter "github.com/json-iterator/go"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

func BenchmarkGetOne_Gjson(b *testing.B) {
Expand Down Expand Up @@ -66,6 +66,103 @@ func BenchmarkGetOne_Sonic(b *testing.B) {
}
}

func BenchmarkGet(b *testing.B) {
b.SetBytes(int64(len(TwitterJson)))
b.Run("gjson", func(b *testing.B) {
for i := 0; i < b.N; i++ {
node := gjson.Get(TwitterJson, "statuses.3.id").Int()
if node != 249279667666817024 {
b.Fail()
}
}
})
b.Run("Node", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := ast.NewSearcher(TwitterJson)
n, _ := s.GetByPath("statuses", 3, "id")
x, _ := n.Int64()
if x != 249279667666817024 {
b.Fatal(n.Interface())
}
}
})
b.Run("Value", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := ast.NewSearcher(TwitterJson)
n, _ := s.GetByPath("statuses", 3, "id")
x, _ := n.Int64()
if x != 249279667666817024 {
b.Fatal(n.Interface())
}
}
})
}

func BenchmarkNodeGet(b *testing.B) {
b.SetBytes(int64(len(TwitterJson)))
b.Run("gjson", func(b *testing.B) {
for i := 0; i < b.N; i++ {
node := gjson.Get(TwitterJson, "statuses")
v := node.Get("3").Get("id").Int()
if v != 249279667666817024 {
b.Fail()
}
}
})
b.Run("Node", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := ast.NewSearcher(TwitterJson)
n, _ := s.GetByPath("statuses")
x, _ := n.Index(3).Get("id").Int64()
if x != 249279667666817024 {
b.Fatal(n.Interface())
}
}
})
b.Run("Value", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := ast.NewSearcher(TwitterJson)
n, _ := s.GetValueByPath("statuses")
x, _ := n.Index(3).Get("id").Int64()
if x != 249279667666817024 {
b.Fatal(n.Interface())
}
}
})
}

func BenchmarkSet(b *testing.B) {
b.SetBytes(int64(len(TwitterJson)))
b.Run("sjson", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := sjson.Set(TwitterJson, "statuses.3.id", 1)
if err != nil {
b.Fatal(err)
}
}
})
b.Run("Node", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := ast.NewSearcher(TwitterJson)
n, _ := s.GetByPath("statuses", 3)
_, err := n.SetAny("id", 1)
if err != nil {
b.Fatal(err)
}
}
})
b.Run("Value", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := ast.NewSearcher(TwitterJson)
_, err := s.SetValueByPath(ast.NewValue(1), "statuses", 3, "id")
if err != nil {
b.Fatal(err)
}
}
})
}



func BenchmarkGetOne_Parallel_Sonic(b *testing.B) {
b.SetBytes(int64(len(TwitterJson)))
Expand Down

0 comments on commit 45f273e

Please sign in to comment.