Add benchmark data

This commit is contained in:
Masaaki Goshima 2021-06-09 20:53:43 +09:00
parent 796f23fb0d
commit 753dc64dff
3 changed files with 289 additions and 24 deletions

View File

@ -7,6 +7,7 @@ replace github.com/goccy/go-json => ../../../
require (
github.com/goccy/go-json v0.0.0-00010101000000-000000000000
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
golang.org/x/tools v0.1.1
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/api v0.47.0
)

View File

@ -351,6 +351,7 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -9,10 +9,12 @@ import (
"log"
"net/http"
"os"
"strings"
"github.com/goccy/go-json"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/tools/benchmark/parse"
"golang.org/x/xerrors"
"google.golang.org/api/option"
"google.golang.org/api/sheets/v4"
@ -23,6 +25,81 @@ const (
benchgraphToken = "benchgraph-token.json"
)
type BenchmarkCodec string
const (
UnknownCodec BenchmarkCodec = "Unknown"
Encoder BenchmarkCodec = "Encode"
Decoder BenchmarkCodec = "Decode"
)
func (c BenchmarkCodec) String() string {
return string(c)
}
type BenchmarkKind string
const (
UnknownKind BenchmarkKind = "Unknown"
SmallStruct BenchmarkKind = "SmallStruct"
SmallStructCached BenchmarkKind = "SmallStructCached"
MediumStruct BenchmarkKind = "MediumStruct"
MediumStructCached BenchmarkKind = "MediumStructCached"
LargeStruct BenchmarkKind = "LargeStruct"
LargeStructCached BenchmarkKind = "LargeStructCached"
)
func (k BenchmarkKind) String() string {
return string(k)
}
type BenchmarkTarget string
const (
UnknownTarget BenchmarkTarget = "Unknown"
EncodingJson BenchmarkTarget = "EncodingJson"
GoJson BenchmarkTarget = "GoJson"
GoJsonNoEscape BenchmarkTarget = "GoJsonNoEscape"
GoJsonColored BenchmarkTarget = "GoJsonColored"
FFJson BenchmarkTarget = "FFJson"
JsonIter BenchmarkTarget = "JsonIter"
EasyJson BenchmarkTarget = "EasyJson"
Jettison BenchmarkTarget = "Jettison"
GoJay BenchmarkTarget = "GoJay"
SegmentioJson BenchmarkTarget = "SegmentioJson"
)
func (t BenchmarkTarget) String() string {
return string(t)
}
func (t BenchmarkTarget) DisplayName() string {
switch t {
case EncodingJson:
return "encoding/json"
case GoJson:
return "goccy/go-json"
case GoJsonNoEscape:
return "goccy/go-json (noescape)"
case GoJsonColored:
return "goccy/go-json (colored)"
case FFJson:
return "pquerna/ffjson"
case JsonIter:
return "json-iterator/go"
case EasyJson:
return "mailru/easyjson"
case Jettison:
return "wl2L/jettison"
case GoJay:
return "francoispqt/gojay"
case SegmentioJson:
return "segmentio/encoding/json"
default:
return ""
}
}
var credFile string
func init() {
@ -45,7 +122,7 @@ func createClient(ctx context.Context) (*http.Client, error) {
}
// If modifying these scopes, delete your previously saved token.json.
config, err := google.ConfigFromJSON(b, sheets.SpreadsheetsScope, slides.PresentationsScope) //"https://spreadsheets.google.com/feeds"
config, err := google.ConfigFromJSON(b, sheets.SpreadsheetsScope, slides.PresentationsScope)
if err != nil {
return nil, xerrors.Errorf("failed to create config from scope: %w", err)
}
@ -158,7 +235,7 @@ func createSpreadsheet(svc *sheets.Service, title string, headers []string, targ
return spreadSheet, nil
}
func generateChart(svc *sheets.Service, spreadSheet *sheets.Spreadsheet, title string) (int64, error) {
func generateChart(svc *sheets.Service, spreadSheet *sheets.Spreadsheet, title string) (*sheets.EmbeddedChart, error) {
sheet := spreadSheet.Sheets[0]
rows := sheet.Data[0].RowData
rowSize := int64(len(rows))
@ -194,7 +271,7 @@ func generateChart(svc *sheets.Service, spreadSheet *sheets.Spreadsheet, title s
Title: title,
BasicChart: &sheets.BasicChartSpec{
ChartType: "BAR",
LegendPosition: "RIGHT_LEGEND",
LegendPosition: "TOP_LEGEND",
Domains: []*sheets.BasicChartDomain{
{
Domain: &sheets.ChartData{
@ -222,16 +299,16 @@ func generateChart(svc *sheets.Service, spreadSheet *sheets.Spreadsheet, title s
},
}).Do()
if err != nil {
return 0, xerrors.Errorf("failed to generate chart: %w", err)
return nil, xerrors.Errorf("failed to generate chart: %w", err)
}
for _, rep := range res.Replies {
if rep.AddChart == nil {
continue
}
return rep.AddChart.Chart.ChartId, nil
return rep.AddChart.Chart, nil
}
return 0, xerrors.Errorf("failed to find chartID")
return nil, xerrors.Errorf("failed to find chartID")
}
func createPresentationWithEmptySlide(presentationService *slides.PresentationsService) (*slides.Presentation, string, error) {
@ -260,19 +337,19 @@ func createPresentationWithEmptySlide(presentationService *slides.PresentationsS
return nil, "", xerrors.Errorf("failed to find slide objectID")
}
func addChartToPresentation(presentationService *slides.PresentationsService, presentation *slides.Presentation, slideID string, spreadSheetID string, chartID int64) error {
func addChartToPresentation(presentationService *slides.PresentationsService, presentation *slides.Presentation, slideID string, spreadSheetID string, chart *sheets.EmbeddedChart) error {
if _, err := presentationService.BatchUpdate(presentation.PresentationId, &slides.BatchUpdatePresentationRequest{
Requests: []*slides.Request{
{
CreateSheetsChart: &slides.CreateSheetsChartRequest{
LinkingMode: "LINKED",
SpreadsheetId: spreadSheetID,
ChartId: chartID,
ChartId: chart.ChartId,
ElementProperties: &slides.PageElementProperties{
PageObjectId: slideID,
Size: &slides.Size{
Width: &slides.Dimension{
Magnitude: 1024,
Magnitude: 1200,
Unit: "PT",
},
Height: &slides.Dimension{
@ -325,32 +402,164 @@ func downloadImage(url, path string) error {
return nil
}
func _main(args []string) error {
ctx := context.Background()
client, err := createClient(ctx)
type BenchmarkData struct {
*parse.Benchmark
Codec BenchmarkCodec
Kind BenchmarkKind
Target BenchmarkTarget
}
func toBenchmarkCodec(name string) (BenchmarkCodec, error) {
switch {
case strings.Contains(name, "_Encode_"):
return Encoder, nil
case strings.Contains(name, "_Decoder_"):
return Decoder, nil
default:
return UnknownCodec, xerrors.Errorf("cannot find codec from %s", name)
}
}
func toBenchmarkKind(name string) (BenchmarkKind, error) {
switch {
case strings.Contains(name, "_SmallStruct_"):
return SmallStruct, nil
case strings.Contains(name, "_MediumStruct_"):
return MediumStruct, nil
case strings.Contains(name, "_LargeStruct_"):
return LargeStruct, nil
case strings.Contains(name, "_SmallStructCached_"):
return SmallStructCached, nil
case strings.Contains(name, "_MediumStructCached_"):
return MediumStructCached, nil
case strings.Contains(name, "_LargeStructCached_"):
return LargeStructCached, nil
default:
return UnknownKind, xerrors.Errorf("cannot find kind from %s", name)
}
}
func toBenchmarkTarget(name string) (BenchmarkTarget, error) {
v := strings.ToLower(name)
switch {
case strings.Contains(v, strings.ToLower("EncodingJson")):
return EncodingJson, nil
case strings.Contains(v, strings.ToLower("GoJson")):
switch {
case strings.Contains(v, strings.ToLower("NoEscape")):
return GoJsonNoEscape, nil
case strings.Contains(v, strings.ToLower("Colored")):
return GoJsonColored, nil
}
return GoJson, nil
case strings.Contains(v, strings.ToLower("FFJson")):
return FFJson, nil
case strings.Contains(v, strings.ToLower("JsonIter")):
return JsonIter, nil
case strings.Contains(v, strings.ToLower("EasyJson")):
return EasyJson, nil
case strings.Contains(v, strings.ToLower("Jettison")):
return Jettison, nil
case strings.Contains(v, strings.ToLower("GoJay")):
return GoJay, nil
case strings.Contains(v, strings.ToLower("SegmentioJson")):
return SegmentioJson, nil
default:
return UnknownTarget, xerrors.Errorf("cannot find target from %s", name)
}
}
func createBenchmarkData(bench *parse.Benchmark) (*BenchmarkData, error) {
codec, err := toBenchmarkCodec(bench.Name)
if err != nil {
return xerrors.Errorf("failed to create client: %w", err)
return nil, xerrors.Errorf("failed to convert benchmark codec: %w", err)
}
kind, err := toBenchmarkKind(bench.Name)
if err != nil {
return nil, xerrors.Errorf("failed to convert benchmark kind: %w", err)
}
target, err := toBenchmarkTarget(bench.Name)
if err != nil {
return nil, xerrors.Errorf("failed to convert benchmark target: %w", err)
}
return &BenchmarkData{
Benchmark: bench,
Codec: codec,
Kind: kind,
Target: target,
}, nil
}
func createAllBenchmarkData(data string) ([]*BenchmarkData, error) {
allBenchData := []*BenchmarkData{}
for _, line := range strings.Split(data, "\n") {
if !strings.HasPrefix(line, "Benchmark") {
continue
}
bench, err := parse.ParseLine(line)
if err != nil {
return nil, xerrors.Errorf("failed to parse line: %w", err)
}
benchData, err := createBenchmarkData(bench)
if err != nil {
return nil, xerrors.Errorf("failed to create benchmark data: %w", err)
}
allBenchData = append(allBenchData, benchData)
}
return allBenchData, nil
}
type Graph struct {
Title string
Codec BenchmarkCodec
Kinds []BenchmarkKind
}
func (g *Graph) existsKind(target BenchmarkKind) bool {
for _, kind := range g.Kinds {
if kind == target {
return true
}
}
return false
}
func generateBenchmarkGraph(ctx context.Context, client *http.Client, g *Graph, data []*BenchmarkData) error {
headers := []string{}
for _, kind := range g.Kinds {
headers = append(headers, kind.String())
}
targetMap := map[string][]*BenchmarkData{}
targetToData := map[string][]float64{}
for _, v := range data {
if g.Codec != v.Codec {
continue
}
if !g.existsKind(v.Kind) {
continue
}
name := v.Target.DisplayName()
targetMap[name] = append(targetMap[name], v)
targetToData[name] = append(targetToData[name], v.NsPerOp)
}
targets := []string{}
for k := range targetMap {
targets = append(targets, k)
}
sheetService, err := sheets.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
return xerrors.Errorf("failed to create service for sheet: %w", err)
}
headers := []string{"TypeA", "TypeB", "TypeC"}
targets := []string{"targetA", "targetB"}
data := map[string][]float64{
targets[0]: []float64{10, 100, 1000},
targets[1]: []float64{20, 200, 2000},
}
spreadSheet, err := createSpreadsheet(sheetService, "go-json benchmark results", headers, targets, data)
spreadSheet, err := createSpreadsheet(sheetService, g.Title, headers, targets, targetToData)
if err != nil {
return xerrors.Errorf("failed to create spreadsheet: %w", err)
}
chartID, err := generateChart(sheetService, spreadSheet, "Benchmark Result")
chart, err := generateChart(sheetService, spreadSheet, g.Title)
if err != nil {
return xerrors.Errorf("failed to generate chart: %w", err)
}
log.Println("spreadSheetID = ", spreadSheet.SpreadsheetId, "chartID = ", chartID)
log.Println("spreadSheetID = ", spreadSheet.SpreadsheetId, "chartID = ", chart.ChartId)
slideService, err := slides.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
@ -361,7 +570,7 @@ func _main(args []string) error {
if err != nil {
return xerrors.Errorf("failed to create presentation with slide: %w", err)
}
if err := addChartToPresentation(presentationService, presentation, slideID, spreadSheet.SpreadsheetId, chartID); err != nil {
if err := addChartToPresentation(presentationService, presentation, slideID, spreadSheet.SpreadsheetId, chart); err != nil {
return xerrors.Errorf("failed to add chart to presentation: %w", err)
}
if err := downloadChartImage(presentationService, presentation, "bench.png"); err != nil {
@ -370,8 +579,62 @@ func _main(args []string) error {
return nil
}
func run(args []string) error {
benchData, err := createAllBenchmarkData(`
goos: darwin
goarch: amd64
pkg: benchmark
Benchmark_Encode_SmallStruct_EncodingJson-16 2135164 555 ns/op 256 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_FFJson-16 1348426 935 ns/op 584 B/op 9 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-16 1970002 598 ns/op 264 B/op 3 allocs/op
Benchmark_Encode_SmallStruct_EasyJson-16 2202872 547 ns/op 720 B/op 4 allocs/op
Benchmark_Encode_SmallStruct_Jettison-16 2610375 461 ns/op 256 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJay-16 2763138 428 ns/op 624 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_SegmentioJson-16 4124536 291 ns/op 256 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJsonColored-16 2530636 475 ns/op 432 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-16 4308301 282 ns/op 256 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJsonNoEscape-16 5406490 215 ns/op 144 B/op 1 allocs/op
Benchmark_Encode_SmallStructCached_EncodingJson-16 2386401 510 ns/op 144 B/op 1 allocs/op
Benchmark_Encode_SmallStructCached_FFJson-16 1450132 829 ns/op 472 B/op 8 allocs/op
Benchmark_Encode_SmallStructCached_JsonIter-16 2279529 526 ns/op 152 B/op 2 allocs/op
Benchmark_Encode_SmallStructCached_EasyJson-16 2225763 543 ns/op 720 B/op 4 allocs/op
Benchmark_Encode_SmallStructCached_Jettison-16 3059923 387 ns/op 144 B/op 1 allocs/op
Benchmark_Encode_SmallStructCached_GoJay-16 3187108 372 ns/op 512 B/op 1 allocs/op
Benchmark_Encode_SmallStructCached_SegmentioJson-16 5128329 229 ns/op 144 B/op 1 allocs/op
Benchmark_Encode_SmallStructCached_GoJsonColored-16 3028646 403 ns/op 320 B/op 1 allocs/op
Benchmark_Encode_SmallStructCached_GoJson-16 5458942 215 ns/op 144 B/op 1 allocs/op
Benchmark_Encode_SmallStructCached_GoJsonNoEscape-16 5725311 210 ns/op 144 B/op 1 allocs/op
PASS
ok benchmark 33.928s
`)
if err != nil {
return xerrors.Errorf("failed to parse benchmark data: %w", err)
}
if benchData == nil {
return nil
}
ctx := context.Background()
client, err := createClient(ctx)
if err != nil {
return xerrors.Errorf("failed to create client: %w", err)
}
graphs := []*Graph{
{
Title: "Encoding Benchmark ( Small / Medium Struct )",
Codec: Encoder,
Kinds: []BenchmarkKind{SmallStruct, MediumStruct},
},
}
for _, graph := range graphs {
if err := generateBenchmarkGraph(ctx, client, graph, benchData); err != nil {
return xerrors.Errorf("failed to generate benchmark graph: %w", err)
}
}
return nil
}
func main() {
if err := _main(os.Args); err != nil {
if err := run(os.Args); err != nil {
log.Fatalf("%+v", err)
}
}