caio.co/de/go-tdigest

Optimize Merge() when the receiver is empty

This makes reusing an existing TDigest (through Reset()) a reasonable
alternative to Clone()

goos: linux
goarch: amd64
pkg: github.com/caio/go-tdigest/v4
cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
              โ”‚   old.txt    โ”‚               new.txt               โ”‚
              โ”‚    sec/op    โ”‚   sec/op     vs base                โ”‚
MergeEmpty-16   98.035ยต ยฑ 2%   3.189ยต ยฑ 6%  -96.75% (p=0.000 n=10)
Id
a7f925ee9cbbe06961c8d45c5f0dac82d8f70685
Author
Caio
Commit time
2025-11-29T14:35:17+01:00

Modified tdigest.go

@@ -264,6 +264,12
return nil
}

+ if t.summary.Len() == 0 && other.compression == t.compression {
+ t.summary = other.summary.Clone()
+ t.count = other.count
+ return nil
+ }
+
other.summary.Perm(t.rng, func(mean float64, count uint64) bool {
err = t.AddWeighted(mean, count)
return err == nil

Modified tdigest_test.go

@@ -802,6 +802,19
}
}

+func BenchmarkMergeEmpty(b *testing.B) {
+ t, _ := New(Compression(100))
+ for n := 0; n < 1000; n++ {
+ t.AddWeighted(rand.Float64(), uint64(rand.Intn(100)))
+ }
+
+ empty, _ := New(Compression(100))
+ for b.Loop() {
+ empty.Reset()
+ empty.Merge(t)
+ }
+}
+
func BenchmarkMerge(b *testing.B) {
b.ReportAllocs()