caio.co/de/go-tdigest

Expose options to change the RNG being used

This patch creates a new public interface `TDigestRNG`
and exposes two new options:

- tdigest.RandomNumberGenerator(TDigestRNG)
- tdigest.LocalRandomNumberGenerator(seed)
Id
191891a7cadb55fc959e85147b70b1857908db8e
Author
Caio
Commit time
2017-10-25T15:59:57+02:00

Modified options.go

@@ -26,3 +26,26
return nil
}
}
+
+// RandomNumberGenerator sets the RNG to be used internally
+//
+// This allows changing which random number source is used when using
+// the TDigest structure (rngs are used when deciding which candidate
+// centroid to merge with and when compressing or merging with
+// another digest for it increases accuracy). This functionality is
+// particularly useful for testing or when you want to disconnect
+// your sample collection from the (default) shared random source
+// to minimize lock contention.
+func RandomNumberGenerator(rng TDigestRNG) tdigestOption {
+ return func(t *TDigest) error {
+ t.rng = rng
+ return nil
+ }
+}
+
+// LocalRandomNumberGenerator makes the TDigest use the default
+// `math/random` functions but with an unshared source that is
+// seeded with the given `seed` parameter.
+func LocalRandomNumberGenerator(seed int64) tdigestOption {
+ return RandomNumberGenerator(newLocalRNG(seed))
+}

Modified options_test.go

@@ -25,3 +25,20
t.Errorf("Trying to create a digest with bad compression should give an error")
}
}
+
+func TestRandomNumberGenerator(t *testing.T) {
+ const numTests = 100
+
+ // Create two digests with unshared rngs seeded with
+ // the same seed
+ t1, _ := New(RandomNumberGenerator(newLocalRNG(0xDEADBEE)))
+ t2, _ := New(LocalRandomNumberGenerator(0xDEADBEE))
+
+ // So that they should emit the same values when called
+ // at the same frequency
+ for i := 0; i < numTests; i++ {
+ if t1.rng.Float32() != t2.rng.Float32() || t1.rng.Intn(10) != t2.rng.Intn(10) {
+ t.Errorf("r1 and r2 should be distinct RNGs returning the same values")
+ }
+ }
+}

Modified rng.go

@@ -22,3 +22,17
type localRNG struct {
localRand *rand.Rand
}
+
+func newLocalRNG(seed int64) *localRNG {
+ return &localRNG{
+ localRand: rand.New(rand.NewSource(seed)),
+ }
+}
+
+func (r *localRNG) Float32() float32 {
+ return r.localRand.Float32()
+}
+
+func (r *localRNG) Intn(i int) int {
+ return r.localRand.Intn(i)
+}