cat / 012
clone

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

Cache selection statistics and sum big ranges by iteration

Dami committed 0f86bf4
The status line computes Sum, Avg and Count of the selection on every
frame. Over 8192x256 numbers that was 2 million map lookups, 90 ms a
frame, so with everything selected each keystroke took 90 ms. Iterating
the cell map costs a quarter of a lookup per cell, so ranges over a
quarter of the sheet's cells now iterate; and the result is kept until
a cell or value changes (a version counter bumped by place and
evaluate).

  Keystroke/select-data/dense-8192x256/80x24   91 ms -> 1.8 ms (steady 0.2 ms)
  Keystroke/extend-data/dense-8192x256/80x24   91 ms -> 29 ms
commit
0f86bf4d0d5f5b48b53e69c670a38a6da60c4c0f
parent
c2188bfb5c213c27788046568ff5a3e1d2b3a9d0
author
Dami <dami.etoile@gmail.com>
3 files changed, +45 -8
Minternal/sheet/nav.go32+++++++++++++++++++++++--------
Minternal/sheet/nav_test.go14+++++++++++++
Minternal/sheet/sheet.go7+++++++
M internal/sheet/nav.go +24 -8
@@ -62,9 +62,24 @@ type Stats struct {
6262 Count, Nums int6363 }6464 65-// RangeStats computes Stats over r, visiting whichever is smaller: the66-// cells in r or the non-blank cells in the sheet.65+// statsCache is the last RangeStats result: the status line asks for the66+// selection's statistics on every frame, and over a whole sheet of two67+// million cells they take tens of milliseconds.68+type statsCache struct {69+ r Rect70+ version uint6471+ st Stats72+ ok bool73+}74+75+// RangeStats computes Stats over r, visiting whichever is cheaper: the76+// cells in r one lookup at a time, or every stored cell (iterating the77+// map costs about a quarter of a lookup per cell). The result is kept78+// until a cell or value changes.6779 func (s *Sheet) RangeStats(r Rect) Stats {80+ if c := s.stats; c.ok && c.r == r && c.version == s.version {81+ return c.st82+ }6883 var st Stats6984 add := func(c *Cell) {7085 st.Count++
@@ -74,7 +89,7 @@ func (s *Sheet) RangeStats(r Rect) Stats {
7489 }7590 }7691 area := (r.To.Col - r.From.Col + 1) * (r.To.Row - r.From.Row + 1)77- if area <= len(s.cells) {92+ if area <= len(s.cells)/4 {7893 for row := r.From.Row; row <= r.To.Row; row++ {7994 for col := r.From.Col; col <= r.To.Col; col++ {8095 if c := s.cells[Addr{Col: col, Row: row}]; !c.Blank() {
@@ -82,12 +97,13 @@ func (s *Sheet) RangeStats(r Rect) Stats {
8297 }8398 }8499 }85- return st86- }87- for a, c := range s.cells {88- if r.Contains(a) && !c.Blank() {89- add(c)100+ } else {101+ for a, c := range s.cells {102+ if r.Contains(a) && !c.Blank() {103+ add(c)104+ }90105 }91106 }107+ s.stats = statsCache{r: r, version: s.version, st: st, ok: true}92108 return st93109 }
M internal/sheet/nav_test.go +14 -0
M internal/sheet/sheet.go +7 -0