Argument helpers returned errors as &v of their Value parameter, which moved the parameter to the heap on every call, not just on errors: two allocations per cell a SUM, AVERAGE or COUNT read. errOf copies the value only when there is an error. Edit/fanin-1000xSUM8192 320 ms, 16.4M allocs -> 203 ms, 2k allocs Edit/running-8192 2.27 s, 67M allocs -> 1.83 s, 16k allocs RecalcAll/fanin 516 ms, 1.2 GB -> 395 ms, 0.7 GB
- commit
- 1a9be26d5cb914f9973659f415bcf8b7a79e6d2c
- parent
- c72b1279fc79d83a4f0bb472dc8d4726b3e5501a
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 8 | ++++---- |
| M | internal/ | 8 | +++++++- |
| M | internal/ | 4 | ++-- |
| M | internal/ | 2 | +- |
@@ -12,7 +12,7 @@ import (
1212 func numArg(n Node, get lookup) (float64, *Value) {1313 v := eval(n, get)1414 if v.Kind == Error {15- return 0, &v15+ return 0, errOf(v)1616 }1717 return toNum(v)1818 }@@ -50,7 +50,7 @@ func intArg(args []Node, i int, def float64, get lookup) (int, *Value) {
5050 func textArg(n Node, get lookup) (string, *Value) {5151 v := eval(n, get)5252 if v.Kind == Error {53- return "", &v53+ return "", errOf(v)5454 }5555 return text(v), nil5656 }@@ -120,7 +120,7 @@ func nums(args []Node, get lookup) ([]float64, *Value) {
120120 e := each(args, get, func(v Value, direct bool) *Value {121121 switch {122122 case v.Kind == Error:123- return &v123+ return errOf(v)124124 case v.Kind == Empty, v.Kind != Number && !direct:125125 return nil126126 }@@ -140,7 +140,7 @@ func texts(args []Node, get lookup) ([]string, *Value) {
140140 var out []string141141 e := each(args, get, func(v Value, _ bool) *Value {142142 if v.Kind == Error {143- return &v143+ return errOf(v)144144 }145145 out = append(out, text(v))146146 return nil@@ -64,6 +64,12 @@ func boolean(b bool) Value {
6464 // toNum coerces v for arithmetic as Sheets does: blanks are 0, booleans6565 // 1 or 0, numeric text (including dates such as "2026-09-26") is its6666 // number, other text is #VALUE!.67+// errOf returns a pointer to a copy of v, for the *Value error results of68+// argument helpers. Taking &v of a parameter directly would move it to69+// the heap on every call, erroneous or not: one allocation per cell read70+// by SUM over a range.71+func errOf(v Value) *Value { return &v }72+6773 func toNum(v Value) (float64, *Value) {6874 switch v.Kind {6975 case Empty:@@ -76,7 +82,7 @@ func toNum(v Value) (float64, *Value) {
7682 }7783 return 0, &ErrValue7884 }79- return 0, &v85+ return 0, errOf(v)8086 }8187 8288 // lookup resolves the current value of a referenced cell.@@ -197,7 +197,7 @@ func aggregate(done func(agg) Value) func([]Node, lookup) Value {
197197 e := each(args, get, func(v Value, direct bool) *Value {198198 switch v.Kind {199199 case Error:200- return &v200+ return errOf(v)201201 case Empty:202202 return nil203203 }@@ -228,7 +228,7 @@ func logical(test func(trues, n int) bool) func([]Node, lookup) Value {
228228 e := each(args, get, func(v Value, direct bool) *Value {229229 switch {230230 case v.Kind == Error:231- return &v231+ return errOf(v)232232 case v.Kind == Empty, v.Kind == Text && !direct:233233 return nil234234 }@@ -125,7 +125,7 @@ func parsedText(n Node, get lookup) (string, *Value) {
125125 v := eval(n, get)126126 switch v.Kind {127127 case Error:128- return "", &v128+ return "", errOf(v)129129 case Text:130130 return v.Str, nil131131 }