- commit
- e899946eda7e9f9ff682559197571ef5026d4838
- parent
- bdc52596b78b33bf8de50f034204c3732688d78f
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 98 | ++++++++++++++++++++++++++---- |
@@ -12,8 +12,10 @@ import (
1212 "os"1313 "path/filepath"1414 "runtime"15+ "runtime/metrics"1516 "strconv"1617 "testing"18+ "time"1719 1820 "github.com/FelineStateMachine/012/internal/sheet"1921 "github.com/FelineStateMachine/012/internal/stress"@@ -61,25 +63,93 @@ func BenchmarkImport(b *testing.B) {
6163 if k, _ := KindOf(name); k == SQLite {6264 opt.Table = "PlaylistTrack" // the biggest table6365 }64- var res *Result65- for b.Loop() {66- if res, err = Import(context.Background(), path, opt); err != nil {67- b.Fatal(err)68- }66+ benchImport(b, path, st.Size(), opt)67+ })68+ }69+}70+71+// BenchmarkImportXLSX imports synthetic workbooks written by 012's72+// exporter: numbers, text and a column of formulas, each a full 819273+// rows.74+func BenchmarkImportXLSX(b *testing.B) {75+ for _, tc := range []struct {76+ name string77+ s *sheet.Sheet78+ }{79+ {"numbers-8192x26", stress.Dense(sheet.MaxRows, 26)},80+ {"table-8192x26", stress.Table(sheet.MaxRows-1, 26)},81+ {"formulas-8192", stress.Chain(sheet.MaxRows)},82+ } {83+ b.Run(tc.name, func(b *testing.B) {84+ path := filepath.Join(b.TempDir(), tc.name+".xlsx")85+ if _, err := Export(context.Background(), path, XLSX, Snap(tc.s, sheet.Rect{}, "data"), ExportOptions{}); err != nil {86+ b.Fatal(err)6987 }70- b.SetBytes(st.Size())71- cells := res.Sheet.Len()72- b.ReportMetric(float64(cells), "cells")73- b.ReportMetric(float64(cells)*float64(b.N)/b.Elapsed().Seconds(), "cells/s")74- res = nil75- before := liveHeap()76- res, _ = Import(context.Background(), path, opt)77- b.ReportMetric(float64(liveHeap()-before)/float64(max(cells, 1)), "B/cell")78- runtime.KeepAlive(res)88+ st, err := os.Stat(path)89+ if err != nil {90+ b.Fatal(err)91+ }92+ benchImport(b, path, st.Size(), Options{})7993 })8094 }8195 }8296 97+// benchImport imports path in a loop and reports throughput, cells kept,98+// the live heap of the imported sheet per cell (B/cell) and the peak99+// heap while importing, per cell (peak-B/cell), sampled.100+func benchImport(b *testing.B, path string, size int64, opt Options) {101+ var res *Result102+ var err error103+ for b.Loop() {104+ if res, err = Import(context.Background(), path, opt); err != nil {105+ b.Fatal(err)106+ }107+ }108+ b.SetBytes(size)109+ cells := res.Sheet.Len()110+ b.ReportMetric(float64(cells), "cells")111+ b.ReportMetric(float64(cells)*float64(b.N)/b.Elapsed().Seconds(), "cells/s")112+ res = nil113+ before := liveHeap()114+ stop := samplePeak()115+ res, _ = Import(context.Background(), path, opt)116+ peak := stop()117+ b.ReportMetric(float64(liveHeap()-before)/float64(max(cells, 1)), "B/cell")118+ b.ReportMetric(float64(peak-min(peak, before))/float64(max(cells, 1)), "peak-B/cell")119+ runtime.KeepAlive(res)120+}121+122+// samplePeak samples the heap every 100 microseconds until the returned123+// function is called, which returns the most seen: live objects and124+// garbage not yet collected, what the process holds.125+func samplePeak() func() uint64 {126+ sample := []metrics.Sample{{Name: "/memory/classes/heap/objects:bytes"}}127+ read := func() uint64 {128+ metrics.Read(sample)129+ return sample[0].Value.Uint64()130+ }131+ done := make(chan struct{})132+ peak := make(chan uint64)133+ go func() {134+ hi := read()135+ t := time.NewTicker(100 * time.Microsecond)136+ defer t.Stop()137+ for {138+ select {139+ case <-done:140+ peak <- max(hi, read())141+ return142+ case <-t.C:143+ hi = max(hi, read())144+ }145+ }146+ }()147+ return func() uint64 {148+ close(done)149+ return <-peak150+ }151+}152+83153 // BenchmarkExport writes a dense numeric sheet in each export format and84154 // reports the file size.85155 func BenchmarkExport(b *testing.B) {