cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Stress: heap per cell after opening a file, and the cost of a cell read

Dami committed 21e41c5
BenchmarkMemory/open-8192x256 measures the live heap per cell of a
sheet opened from a .012 file; BenchmarkRead the cost of reading one
cell through Sheet.Value and as formulas read cells in recalculation.
commit
21e41c5fb4a4bbd38c9be2ab5f69a9a45f0c8be2
parent
24d313d458adc9a8a0311d3deba8fb68ea2318f3
author
Dami <dami.etoile@gmail.com>
1 file changed, +61 -5
Minternal/sheet/stress_test.go66++++++++++++++++++++++++++++--
M internal/sheet/stress_test.go +61 -5
@@ -9,6 +9,7 @@ import (
99 "bytes"1010 "fmt"1111 "runtime"12+ "sync"1213 "testing"1314 1415 "github.com/FelineStateMachine/012/internal/sheet"
@@ -246,14 +247,29 @@ func saveShapes() []namedBuild {
246247 }247248 }248249 249-// BenchmarkMemory reports the live heap per non-blank cell after a load.250+// BenchmarkMemory reports the live heap per non-blank cell after a load:251+// built through Load, or opened from a .012 file written beforehand.250252 func BenchmarkMemory(b *testing.B) {251- for _, sh := range []namedBuild{252- {"numbers", func() *sheet.Sheet { return stress.Dense(stress.Rows, 64) }},253- {"formulas", func() *sheet.Sheet { return stress.Chain(stress.Rows) }},254- {"text-200", func() *sheet.Sheet { return stress.LongText(stress.Rows, 200) }},253+ for _, sh := range []struct {254+ name string255+ prep func()256+ build func() *sheet.Sheet257+ }{258+ {"numbers", nil, func() *sheet.Sheet { return stress.Dense(stress.Rows, 64) }},259+ {"formulas", nil, func() *sheet.Sheet { return stress.Chain(stress.Rows) }},260+ {"text-200", nil, func() *sheet.Sheet { return stress.LongText(stress.Rows, 200) }},261+ {"open-8192x256", func() { denseFile() }, func() *sheet.Sheet {262+ s, err := sheet.Read(bytes.NewReader(denseFile()))263+ if err != nil {264+ b.Fatal(err)265+ }266+ return s267+ }},255268 } {256269 b.Run(sh.name, func(b *testing.B) {270+ if sh.prep != nil {271+ sh.prep()272+ }257273 for b.Loop() {258274 before := heap()259275 s := sh.build()
@@ -265,6 +281,46 @@ func BenchmarkMemory(b *testing.B) {
265281 }266282 }267283 284+// denseFile is the .012 file of 8192x256 numbers.285+var denseFile = sync.OnceValue(func() []byte {286+ var buf bytes.Buffer287+ if err := stress.Dense(stress.Rows, stress.Cols).Write(&buf); err != nil {288+ panic(err)289+ }290+ return buf.Bytes()291+})292+293+// BenchmarkRead is the cost of reading a cell's value, per cell of 8192294+// x 26 numbers: through Sheet.Value, and as formulas read cells in a295+// recalculation (a SUM of each column, read directly).296+func BenchmarkRead(b *testing.B) {297+ s := stress.Dense(stress.Rows, 26)298+ cells := float64(stress.Rows * 26)299+ b.Run("value", func(b *testing.B) {300+ sum := 0.0301+ for b.Loop() {302+ for row := range stress.Rows {303+ for col := range 26 {304+ sum += s.Value(sheet.Addr{Col: col, Row: row}).Num305+ }306+ }307+ }308+ b.ReportMetric(float64(b.Elapsed().Nanoseconds())/float64(b.N)/cells, "ns/cell")309+ })310+ b.Run("recalc", func(b *testing.B) {311+ for col := range 26 {312+ name := sheet.ColName(col)313+ if err := s.Set(sheet.Addr{Col: col, Row: stress.Rows}, fmt.Sprintf("=SUM(%s1:%s%d)", name, name, stress.Rows)); err != nil {314+ b.Fatal(err)315+ }316+ }317+ for b.Loop() {318+ s.RecalcAll()319+ }320+ b.ReportMetric(float64(b.Elapsed().Nanoseconds())/float64(b.N)/cells, "ns/cell")321+ })322+}323+268324 // heap is the live heap after a full collection.269325 func heap() uint64 {270326 runtime.GC()