Benchmark marshal (#550)
This commit is contained in:
@@ -156,14 +156,17 @@ Execution time speedup compared to other Go TOML libraries:
|
||||
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>HugoFrontMatter</td><td>2.6x</td><td>2.2x</td></tr>
|
||||
<tr><td>ReferenceFile/map</td><td>2.8x</td><td>3.0x</td></tr>
|
||||
<tr><td>ReferenceFile/struct</td><td>5.4x</td><td>6.2x</td></tr>
|
||||
<tr><td>Marshal/HugoFrontMatter</td><td>1.9x</td><td>1.9x</td></tr>
|
||||
<tr><td>Marshal/ReferenceFile/map</td><td>1.7x</td><td>1.9x</td></tr>
|
||||
<tr><td>Marshal/ReferenceFile/struct</td><td>2.7x</td><td>2.9x</td></tr>
|
||||
<tr><td>Unmarshal/HugoFrontMatter</td><td>2.9x</td><td>2.4x</td></tr>
|
||||
<tr><td>Unmarshal/ReferenceFile/map</td><td>3.1x</td><td>3.0x</td></tr>
|
||||
<tr><td>Unmarshal/ReferenceFile/struct</td><td>5.5x</td><td>5.8x</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<details><summary>See more</summary>
|
||||
<p>The table above has the results of the most common use-cases. The table
|
||||
below contains the results of all benchmarks, including unrealistic ones. is
|
||||
<p>The table above has the results of the most common use-cases. The table below
|
||||
contains the results of all benchmarks, including unrealistic ones. It is
|
||||
provided for completeness.</p>
|
||||
|
||||
<table>
|
||||
@@ -171,14 +174,16 @@ provided for completeness.</p>
|
||||
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>UnmarshalSimple/map</td><td>3.8x</td><td>2.4x</td></tr>
|
||||
<tr><td>UnmarshalSimple/struct</td><td>5.4x</td><td>3.1x</td></tr>
|
||||
<tr><td>UnmarshalDataset/example</td><td>2.8x</td><td>2.0x</td></tr>
|
||||
<tr><td>UnmarshalDataset/code</td><td>1.8x</td><td>2.2x</td></tr>
|
||||
<tr><td>UnmarshalDataset/twitter</td><td>2.5x</td><td>1.8x</td></tr>
|
||||
<tr><td>UnmarshalDataset/citm_catalog</td><td>1.9x</td><td>1.2x</td></tr>
|
||||
<tr><td>UnmarshalDataset/config</td><td>3.0x</td><td>2.5x</td></tr>
|
||||
<tr><td>[Geo mean]</td><td>3.0x</td><td>2.4x</td></tr>
|
||||
<tr><td>Marshal/SimpleDocument/map</td><td>1.8x</td><td>2.4x</td></tr>
|
||||
<tr><td>Marshal/SimpleDocument/struct</td><td>2.7x</td><td>3.5x</td></tr>
|
||||
<tr><td>Unmarshal/SimpleDocument/map</td><td>4.3x</td><td>2.4x</td></tr>
|
||||
<tr><td>Unmarshal/SimpleDocument/struct</td><td>5.8x</td><td>3.3x</td></tr>
|
||||
<tr><td>UnmarshalDataset/example</td><td>3.1x</td><td>2.2x</td></tr>
|
||||
<tr><td>UnmarshalDataset/code</td><td>1.8x</td><td>2.1x</td></tr>
|
||||
<tr><td>UnmarshalDataset/twitter</td><td>2.7x</td><td>1.9x</td></tr>
|
||||
<tr><td>UnmarshalDataset/citm_catalog</td><td>1.8x</td><td>1.2x</td></tr>
|
||||
<tr><td>UnmarshalDataset/config</td><td>3.4x</td><td>2.8x</td></tr>
|
||||
<tr><td>[Geo mean]</td><td>2.8x</td><td>2.5x</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>
|
||||
|
||||
+194
-59
@@ -1,6 +1,7 @@
|
||||
package benchmark_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -21,15 +22,101 @@ func TestUnmarshalSimple(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalSimple(b *testing.B) {
|
||||
doc := []byte(`A = "hello"`)
|
||||
func BenchmarkUnmarshal(b *testing.B) {
|
||||
b.Run("SimpleDocument", func(b *testing.B) {
|
||||
doc := []byte(`A = "hello"`)
|
||||
|
||||
b.Run("struct", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(doc)))
|
||||
b.Run("struct", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(doc)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := struct {
|
||||
A string
|
||||
}{}
|
||||
|
||||
err := toml.Unmarshal(doc, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("map", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(doc)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(doc, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
b.Run("ReferenceFile", func(b *testing.B) {
|
||||
bytes, err := ioutil.ReadFile("benchmark.toml")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.Run("struct", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(bytes)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := benchmarkDoc{}
|
||||
err := toml.Unmarshal(bytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("map", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(bytes)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(bytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
b.Run("HugoFrontMatter", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(hugoFrontMatterbytes)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(hugoFrontMatterbytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func marshal(v interface{}) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
enc := toml.NewEncoder(&b)
|
||||
err := enc.Encode(v)
|
||||
return b.Bytes(), err
|
||||
}
|
||||
|
||||
func BenchmarkMarshal(b *testing.B) {
|
||||
b.Run("SimpleDocument", func(b *testing.B) {
|
||||
doc := []byte(`A = "hello"`)
|
||||
|
||||
b.Run("struct", func(b *testing.B) {
|
||||
d := struct {
|
||||
A string
|
||||
}{}
|
||||
@@ -38,21 +125,114 @@ func BenchmarkUnmarshalSimple(b *testing.B) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("map", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(doc)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var out []byte
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
out, err = marshal(d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(out)))
|
||||
})
|
||||
|
||||
b.Run("map", func(b *testing.B) {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(doc, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
var out []byte
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
out, err = marshal(d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(out)))
|
||||
})
|
||||
})
|
||||
|
||||
b.Run("ReferenceFile", func(b *testing.B) {
|
||||
bytes, err := ioutil.ReadFile("benchmark.toml")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.Run("struct", func(b *testing.B) {
|
||||
d := benchmarkDoc{}
|
||||
err := toml.Unmarshal(bytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
var out []byte
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
out, err = marshal(d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(out)))
|
||||
})
|
||||
|
||||
b.Run("map", func(b *testing.B) {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(bytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
var out []byte
|
||||
for i := 0; i < b.N; i++ {
|
||||
out, err = marshal(d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(out)))
|
||||
})
|
||||
})
|
||||
|
||||
b.Run("HugoFrontMatter", func(b *testing.B) {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(hugoFrontMatterbytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
var out []byte
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
out, err = marshal(d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(out)))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -163,40 +343,7 @@ type benchmarkDoc struct {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReferenceFile(b *testing.B) {
|
||||
bytes, err := ioutil.ReadFile("benchmark.toml")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.Run("struct", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(bytes)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := benchmarkDoc{}
|
||||
err := toml.Unmarshal(bytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("map", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(bytes)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(bytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestReferenceFile(t *testing.T) {
|
||||
func TestUnmarshalReferenceFile(t *testing.T) {
|
||||
bytes, err := ioutil.ReadFile("benchmark.toml")
|
||||
require.NoError(t, err)
|
||||
d := benchmarkDoc{}
|
||||
@@ -483,8 +630,7 @@ trimmed in raw strings.
|
||||
require.Equal(t, expected, d)
|
||||
}
|
||||
|
||||
func BenchmarkHugoFrontMatter(b *testing.B) {
|
||||
bytes := []byte(`
|
||||
var hugoFrontMatterbytes = []byte(`
|
||||
categories = ["Development", "VIM"]
|
||||
date = "2012-04-06"
|
||||
description = "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
|
||||
@@ -506,14 +652,3 @@ show_comments = false
|
||||
[cascade._target]
|
||||
kind = "section"
|
||||
`)
|
||||
b.SetBytes(int64(len(bytes)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
d := map[string]interface{}{}
|
||||
err := toml.Unmarshal(bytes, &d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,11 +204,18 @@ def printtable(data):
|
||||
</table>""")
|
||||
|
||||
|
||||
fold = 3
|
||||
printtable(results[:fold])
|
||||
def match(x):
|
||||
return "ReferenceFile" in x[0] or "HugoFrontMatter" in x[0]
|
||||
|
||||
above = [x for x in results if match(x)]
|
||||
below = [x for x in results if not match(x)]
|
||||
|
||||
printtable(above)
|
||||
print("<details><summary>See more</summary>")
|
||||
print('<p>The table above has the results of the most common use-cases. The table below contains the results of all benchmarks, including unrealistic ones. is provided for completeness.</p>')
|
||||
printtable(results[fold:])
|
||||
print("""<p>The table above has the results of the most common use-cases. The table below
|
||||
contains the results of all benchmarks, including unrealistic ones. It is
|
||||
provided for completeness.</p>""")
|
||||
printtable(below)
|
||||
print('<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>')
|
||||
print("</details>")
|
||||
|
||||
|
||||
@@ -641,6 +641,9 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte
|
||||
}
|
||||
|
||||
func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
|
||||
if !v.IsValid() {
|
||||
return false
|
||||
}
|
||||
if v.Type() == timeType || v.Type().Implements(textMarshalerType) {
|
||||
return false
|
||||
}
|
||||
|
||||
+5
-1
@@ -290,7 +290,11 @@ func (d *decoder) handleArrayTableCollectionLast(key ast.Iterator, v reflect.Val
|
||||
|
||||
return v, nil
|
||||
case reflect.Slice:
|
||||
elem := reflect.New(v.Type().Elem()).Elem()
|
||||
elemType := v.Type().Elem()
|
||||
if elemType.Kind() == reflect.Interface {
|
||||
elemType = mapStringInterfaceType
|
||||
}
|
||||
elem := reflect.New(elemType).Elem()
|
||||
elem2, err := d.handleArrayTable(key, elem)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
|
||||
+15
-1
@@ -688,7 +688,7 @@ B = "data"`,
|
||||
"Name": "Hammer",
|
||||
"Sku": int64(738594937),
|
||||
},
|
||||
nil,
|
||||
map[string]interface{}(nil),
|
||||
map[string]interface{}{
|
||||
"Name": "Nail",
|
||||
"Sku": int64(284758393),
|
||||
@@ -1201,6 +1201,20 @@ B = "data"`,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "empty array table in interface{}",
|
||||
input: `[[products]]`,
|
||||
gen: func() test {
|
||||
return test{
|
||||
target: &map[string]interface{}{},
|
||||
expected: &map[string]interface{}{
|
||||
"products": []interface{}{
|
||||
map[string]interface{}(nil),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "into map with invalid key type",
|
||||
input: `A = "hello"`,
|
||||
|
||||
Reference in New Issue
Block a user