Skip to content

Commit

Permalink
Benchmark marshal (#550)
Browse files Browse the repository at this point in the history
  • Loading branch information
pelletier authored Jun 2, 2021
1 parent b0d6c62 commit f3bb20e
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 75 deletions.
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,29 +156,34 @@ 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>
<thead>
<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>
Expand Down
247 changes: 191 additions & 56 deletions benchmark/benchmark_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package benchmark_test

import (
"bytes"
"io/ioutil"
"testing"
"time"
Expand All @@ -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.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("struct", func(b *testing.B) {
b.SetBytes(int64(len(doc)))
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
}{}
Expand All @@ -38,21 +125,114 @@ func BenchmarkUnmarshalSimple(b *testing.B) {
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(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("map", func(b *testing.B) {
b.SetBytes(int64(len(doc)))
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++ {
d := map[string]interface{}{}
err := toml.Unmarshal(doc, &d)
out, err = marshal(d)
if err != nil {
panic(err)
}
}

b.SetBytes(int64(len(out)))
})
}

Expand Down Expand Up @@ -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{}
Expand Down Expand Up @@ -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."
Expand All @@ -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)
}
}
}
15 changes: 11 additions & 4 deletions ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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>")
Expand Down
3 changes: 3 additions & 0 deletions marshaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
6 changes: 5 additions & 1 deletion unmarshaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit f3bb20e

Please sign in to comment.