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