cat / 012
clone

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

cat/module-path
012 / internal / fileio / source_stress_test.go
source_stress_test.go 3.2K
1//go:build stress23// Stress benchmarks of sources: a Parquet file and a SQLite table of ten4// million rows, generated once into $STRESS_DIR/generated (or a5// temporary directory), read in place: opening, summing a column as SUM6// does, a page deep in the file, and building and paging a sorted and a7// filtered view, each with the peak heap it took. Timed under the bench8// lock (/tmp/012-bench.lock), as the speed gate is.9package fileio1011import (12 "context"13 "math/rand/v2"14 "testing"1516 "src.rest/cat/012/internal/sheet"17 "src.rest/cat/012/internal/stress"18)1920// bigRows is the rows of the generated sources (stress.SalesParquet).21const bigRows = 10_000_0002223// BenchmarkSource reads ten million rows of each kind of source.24func BenchmarkSource(b *testing.B) {25 dir := stress.GeneratedDir(b.TempDir())26 pq, err := stress.SalesParquet(dir, bigRows)27 if err != nil {28 b.Fatal(err)29 }30 db, err := stress.SalesSQLite(dir, bigRows)31 if err != nil {32 b.Fatal(err)33 }34 files := map[string]SourceSpec{"parquet": {Path: pq}, "sqlite": {Path: db}}35 defer stress.BenchLock(b)()36 for _, kind := range []string{"parquet", "sqlite"} {37 spec := files[kind]38 spec.TempDir = b.TempDir()39 b.Run(kind, func(b *testing.B) { benchSource(b, spec) })40 }41}4243// sortByAmount and northOnly are the views measured.44var (45 sortByAmount = SourceOrder{Sort: []SourceSort{{Col: 2, Desc: true}}}46 northOnly = SourceOrder{Filter: []SourceFilter{{Col: 1, Cond: sheet.Condition{Op: sheet.CondExactly, Arg: "north"}}}}47)4849func benchSource(b *testing.B, spec SourceSpec) {50 ctx := context.Background()51 b.Run("open", func(b *testing.B) {52 measure(b, func() {53 src, err := OpenSource(ctx, spec)54 if err != nil {55 b.Fatal(err)56 }57 if src.Rows() != bigRows {58 b.Fatalf("%d rows", src.Rows())59 }60 src.Close()61 })62 })63 src, err := OpenSource(ctx, spec)64 if err != nil {65 b.Fatal(err)66 }67 defer src.Close()68 b.Run("sum-column", func(b *testing.B) {69 measure(b, func() {70 total := 0.071 err := src.Scan(ctx, 0, []int{2}, func(_ int64, v []sheet.LiveCell) bool {72 total += v[0].V.Num73 return true74 })75 if err != nil {76 b.Fatal(err)77 }78 })79 b.ReportMetric(float64(bigRows)*float64(b.N)/b.Elapsed().Seconds(), "rows/s")80 })81 for _, o := range []struct {82 name string83 order SourceOrder84 }{{"plain", SourceOrder{}}, {"sorted", sortByAmount}, {"filtered", northOnly}} {85 var v SourceView86 b.Run("view-"+o.name, func(b *testing.B) {87 measure(b, func() {88 if v != nil {89 v.Close()90 }91 if v, err = src.View(ctx, o.order); err != nil {92 b.Fatal(err)93 }94 })95 b.ReportMetric(float64(v.Rows()), "rows")96 })97 b.Run("page-"+o.name, func(b *testing.B) {98 r := rand.New(rand.NewPCG(1, 2))99 measure(b, func() {100 from := v.Rows()/2 + r.Int64N(v.Rows()/2-60)101 nums, _, err := v.Page(ctx, from, 60)102 if err != nil || len(nums) != 60 {103 b.Fatalf("page at %d: %d rows, %v", from, len(nums), err)104 }105 })106 })107 v.Close()108 }109}110111// measure runs fn in the benchmark's loop, reporting the peak heap it112// took above what was live before.113func measure(b *testing.B, fn func()) {114 before := liveHeap()115 stop := samplePeak()116 for b.Loop() {117 fn()118 }119 peak := stop()120 b.ReportMetric(float64(peak-min(peak, before))/(1<<20), "peak-MB")121}