Skip to content

Instantly share code, notes, and snippets.

@tiendc
Created July 4, 2023 10:58
Show Gist options
  • Save tiendc/c394677a846233bf8de819da3bb7093c to your computer and use it in GitHub Desktop.
Save tiendc/c394677a846233bf8de819da3bb7093c to your computer and use it in GitHub Desktop.
csvlib_benchmark
package benchmark
import (
"bytes"
"encoding/csv"
"testing"
"github.com/gocarina/gocsv"
"github.com/jszwec/csvutil"
"github.com/tiendc/go-csvlib"
"github.com/yunabe/easycsv"
)
// NOTE: This benchmark code is copied from the bench test of the project https://github.com/jszwec/csvutil
// Unmarshal bench: https://gist.github.com/jszwec/e8515e741190454fa3494bcd3e1f100f
// Marshal bench: https://gist.github.com/jszwec/31980321e1852ebb5615a44ccf374f17
func BenchmarkUnmarshal(b *testing.B) {
type A struct {
A int `csv:"a" name:"a"`
B float64 `csv:"b" name:"b"`
C string `csv:"c" name:"c"`
D int64 `csv:"d" name:"d"`
E int8 `csv:"e" name:"e"`
F float32 `csv:"f" name:"f"`
G float32 `csv:"g" name:"g"`
H float32 `csv:"h" name:"h"`
I string `csv:"i" name:"i"`
J int `csv:"j" name:"j"`
}
fixture := []struct {
desc string
records int
}{
{
desc: "100 records",
records: 100,
},
{
desc: "1000 records",
records: 1000,
},
{
desc: "10000 records",
records: 10000,
},
{
desc: "100000 records",
records: 100000,
},
}
tests := []struct {
desc string
fn func([]byte, *testing.B)
}{
{
desc: "csvlib.Unmarshal",
fn: func(data []byte, b *testing.B) {
var a []A
if _, err := csvlib.Unmarshal(data, &a); err != nil {
b.Error(err)
}
},
},
{
desc: "csvutil.Unmarshal",
fn: func(data []byte, b *testing.B) {
var a []A
if err := csvutil.Unmarshal(data, &a); err != nil {
b.Error(err)
}
},
},
{
desc: "gocsv.Unmarshal",
fn: func(data []byte, b *testing.B) {
var a []A
if err := gocsv.UnmarshalBytes(data, &a); err != nil {
b.Error(err)
}
},
},
{
desc: "easycsv.ReadAll",
fn: func(data []byte, b *testing.B) {
r := easycsv.NewReader(bytes.NewReader(data))
var a []A
if err := r.ReadAll(&a); err != nil {
b.Error(err)
}
},
},
}
for _, t := range tests {
b.Run(t.desc, func(b *testing.B) {
for _, f := range fixture {
b.Run(f.desc, func(b *testing.B) {
data := genData(f.records)
for i := 0; i < b.N; i++ {
t.fn(data, b)
}
})
}
})
}
}
func genData(records int) []byte {
header := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
record := []string{"1", "2.5", "foo", "6", "7", "8", "9", "10", "bar", "10"}
var buf bytes.Buffer
w := csv.NewWriter(&buf)
w.Write(header)
for i := 0; i < records; i++ {
w.Write(record)
}
w.Flush()
if err := w.Error(); err != nil {
panic(err)
}
return buf.Bytes()
}
func BenchmarkMarshal(b *testing.B) {
type A struct {
A int `csv:"a"`
B float64 `csv:"b"`
C string `csv:"c"`
D int64 `csv:"d"`
E int8 `csv:"e"`
F float32 `csv:"f"`
G float32 `csv:"g"`
H float32 `csv:"h"`
I string `csv:"i"`
J int `csv:"j"`
}
fixture := []struct {
desc string
records int
}{
{
desc: "100 records",
records: 100,
},
{
desc: "1000 records",
records: 1000,
},
{
desc: "10000 records",
records: 10000,
},
{
desc: "100000 records",
records: 100000,
},
}
tests := []struct {
desc string
fn func([]A, *testing.B)
}{
{
desc: "csvlib.Marshal",
fn: func(as []A, b *testing.B) {
if _, err := csvlib.Marshal(as); err != nil {
b.Error(err)
}
},
},
{
desc: "csvutil.Marshal",
fn: func(as []A, b *testing.B) {
if _, err := csvutil.Marshal(as); err != nil {
b.Error(err)
}
},
},
{
desc: "gocsv.Marshal",
fn: func(as []A, b *testing.B) {
var buf bytes.Buffer
if err := gocsv.Marshal(as, &buf); err != nil {
b.Error(err)
}
},
},
}
a := A{
A: 1,
B: 3.14,
C: "string",
D: 1,
E: 1,
F: 3.13,
G: 3.14,
H: 3.14,
I: "string",
J: 10,
}
for _, t := range tests {
b.Run(t.desc, func(b *testing.B) {
for _, f := range fixture {
b.Run(f.desc, func(b *testing.B) {
as := make([]A, f.records)
for i := range as {
as[i] = a
}
for i := 0; i < b.N; i++ {
t.fn(as, b)
}
})
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment