caio.co/de/go-tdigest

improved sumUntil implementation

Significant speedup for sumUntil. About half the improvement comes from changing to a conventional integer-based for, and half from unrolling the loop.
Id
fdeb4f7e0951eccb75fb089300ade29bd8768327
Author
ianwilkes
Commit time
2018-10-12T13:15:24-07:00

Modified summary.go

@@ -145,15 +145,22
return item, s.At(idx - 1)
}

-func (s summary) sumUntilMean(mean float64) uint64 {
+// This method is the hotspot when calling Add(), which in turn is called by
+// Compress() and Merge(). A simple loop unroll saves a surprising amount of
+// time.
+func (s summary) sumUntilIndex(idx int) uint64 {
var cumSum uint64
- for i := range s.keys {
- if s.keys[i] < mean {
- cumSum += s.counts[i]
- } else {
- break
- }
+ var i int
+ for i = idx - 1; i >= 3; i -= 4 {
+ cumSum += s.counts[i]
+ cumSum += s.counts[i-1]
+ cumSum += s.counts[i-2]
+ cumSum += s.counts[i-3]
}
+ for ; i >= 0; i-- {
+ cumSum += s.counts[i]
+ }
+
return cumSum
}

Modified tdigest.go

@@ -211,7 +211,7
}

func (t *TDigest) computeCentroidQuantile(c *centroid) float64 {
- cumSum := t.summary.sumUntilMean(c.mean)
+ cumSum := t.summary.sumUntilIndex(c.index)
return (float64(c.count)/2.0 + float64(cumSum)) / float64(t.count)
}