Codebase list golang-github-beorn7-perks / dab8af3
Use a map for targets. Bjoern Rabenstein 9 years ago
3 changed file(s) with 43 addition(s) and 46 deletion(s). Raw diff Collapse all Expand all
66 func BenchmarkInsertTargeted(b *testing.B) {
77 b.ReportAllocs()
88
9 s := NewTargeted(Targets...)
9 s := NewTargeted(Targets)
1010 b.ResetTimer()
1111 for i := float64(0); i < float64(b.N); i++ {
1212 s.Insert(i)
1414 }
1515
1616 func BenchmarkInsertTargetedSmallEpsilon(b *testing.B) {
17 s := NewTargeted(TargetsSmallEpsilon...)
17 s := NewTargeted(TargetsSmallEpsilon)
1818 b.ResetTimer()
1919 for i := float64(0); i < float64(b.N); i++ {
2020 s.Insert(i)
3838 }
3939
4040 func BenchmarkQuery(b *testing.B) {
41 s := NewTargeted(Targets...)
41 s := NewTargeted(Targets)
4242 for i := float64(0); i < 1e6; i++ {
4343 s.Insert(i)
4444 }
5050 }
5151
5252 func BenchmarkQuerySmallEpsilon(b *testing.B) {
53 s := NewTargeted(TargetsSmallEpsilon...)
53 s := NewTargeted(TargetsSmallEpsilon)
5454 for i := float64(0); i < 1e6; i++ {
5555 s.Insert(i)
5656 }
3333 func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
3434 func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
3535
36 // Target describes a quantile for a targeted stream together with its absolute
37 // error, i.e. the true quantile of a value returned by a query is guaranteed to
38 // be within (Quantile±Epsilon).
39 type Target struct {
40 Quantile float64
41 Epsilon float64
42 }
43
4436 type invariant func(s *stream, r float64) float64
4537
4638 // NewLowBiased returns an initialized Stream for low-biased quantiles
7971
8072 // NewTargeted returns an initialized Stream concerned with a particular set of
8173 // quantile values that are supplied a priori. Knowing these a priori reduces
82 // space and computation time.
74 // space and computation time. The targets map maps the desired quantiles to
75 // their absolute errors, i.e. the true quantile of a value returned by a query
76 // is guaranteed to be within (Quantile±Epsilon).
8377 //
8478 // See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
85 func NewTargeted(targets ...Target) *Stream {
79 func NewTargeted(targets map[float64]float64) *Stream {
8680 ƒ := func(s *stream, r float64) float64 {
8781 var m = math.MaxFloat64
8882 var f float64
89 for _, t := range targets {
90 if t.Quantile*s.n <= r {
91 f = (2 * t.Epsilon * r) / t.Quantile
83 for quantile, epsilon := range targets {
84 if quantile*s.n <= r {
85 f = (2 * epsilon * r) / quantile
9286 } else {
93 f = (2 * t.Epsilon * (s.n - r)) / (1 - t.Quantile)
87 f = (2 * epsilon * (s.n - r)) / (1 - quantile)
9488 }
9589 if f < m {
9690 m = f
151145
152146 // Merge merges samples into the underlying streams samples. This is handy when
153147 // merging multiple streams from separate threads, database shards, etc.
148 //
149 // ATTENTION: This method is broken and does not yield correct results. The
150 // underlying algorithm is not capable of merging streams correctly.
154151 func (s *Stream) Merge(samples Samples) {
155152 sort.Sort(samples)
156153 s.stream.merge(samples)
77 )
88
99 var (
10 Targets = []Target{
11 {Quantile: 0.01, Epsilon: 0.001},
12 {Quantile: 0.10, Epsilon: 0.01},
13 {Quantile: 0.50, Epsilon: 0.05},
14 {Quantile: 0.90, Epsilon: 0.01},
15 {Quantile: 0.99, Epsilon: 0.001},
10 Targets = map[float64]float64{
11 0.01: 0.001,
12 0.10: 0.01,
13 0.50: 0.05,
14 0.90: 0.01,
15 0.99: 0.001,
1616 }
17 TargetsSmallEpsilon = []Target{
18 {Quantile: 0.01, Epsilon: 0.0001},
19 {Quantile: 0.10, Epsilon: 0.001},
20 {Quantile: 0.50, Epsilon: 0.005},
21 {Quantile: 0.90, Epsilon: 0.001},
22 {Quantile: 0.99, Epsilon: 0.0001},
17 TargetsSmallEpsilon = map[float64]float64{
18 0.01: 0.0001,
19 0.10: 0.001,
20 0.50: 0.005,
21 0.90: 0.001,
22 0.99: 0.0001,
2323 }
2424 LowQuantiles = []float64{0.01, 0.1, 0.5}
2525 HighQuantiles = []float64{0.99, 0.9, 0.5}
2929
3030 func verifyPercsWithAbsoluteEpsilon(t *testing.T, a []float64, s *Stream) {
3131 sort.Float64s(a)
32 for _, tt := range Targets {
32 for quantile, epsilon := range Targets {
3333 n := float64(len(a))
34 k := int(tt.Quantile * n)
35 lower := int((tt.Quantile - tt.Epsilon) * n)
34 k := int(quantile * n)
35 lower := int((quantile - epsilon) * n)
3636 if lower < 1 {
3737 lower = 1
3838 }
39 upper := int(math.Ceil((tt.Quantile + tt.Epsilon) * n))
39 upper := int(math.Ceil((quantile + epsilon) * n))
4040 if upper > len(a) {
4141 upper = len(a)
4242 }
4343 w, min, max := a[k-1], a[lower-1], a[upper-1]
44 if g := s.Query(tt.Quantile); g < min || g > max {
45 t.Errorf("q=%f: want %v [%f,%f], got %v", tt.Quantile, w, min, max, g)
44 if g := s.Query(quantile); g < min || g > max {
45 t.Errorf("q=%f: want %v [%f,%f], got %v", quantile, w, min, max, g)
4646 }
4747 }
4848 }
9393
9494 func TestTargetedQuery(t *testing.T) {
9595 rand.Seed(42)
96 s := NewTargeted(Targets...)
96 s := NewTargeted(Targets)
9797 a := populateStream(s)
9898 verifyPercsWithAbsoluteEpsilon(t, a, s)
9999 }
114114
115115 func TestTargetedMerge(t *testing.T) {
116116 rand.Seed(42)
117 s1 := NewTargeted(Targets...)
118 s2 := NewTargeted(Targets...)
117 s1 := NewTargeted(Targets)
118 s2 := NewTargeted(Targets)
119119 a := populateStream(s1)
120120 a = append(a, populateStream(s2)...)
121121 s1.Merge(s2.Samples())
143143 }
144144
145145 func TestUncompressed(t *testing.T) {
146 q := NewTargeted(Targets...)
146 q := NewTargeted(Targets)
147147 for i := 100; i > 0; i-- {
148148 q.Insert(float64(i))
149149 }
151151 t.Errorf("want count 100, got %d", g)
152152 }
153153 // Before compression, Query should have 100% accuracy.
154 for _, tt := range Targets {
155 w := tt.Quantile * 100
156 if g := q.Query(tt.Quantile); g != w {
154 for quantile := range Targets {
155 w := quantile * 100
156 if g := q.Query(quantile); g != w {
157157 t.Errorf("want %f, got %f", w, g)
158158 }
159159 }
160160 }
161161
162162 func TestUncompressedSamples(t *testing.T) {
163 q := NewTargeted(Target{0.99, 0.001})
163 q := NewTargeted(map[float64]float64{0.99: 0.001})
164164 for i := 1; i <= 100; i++ {
165165 q.Insert(float64(i))
166166 }
170170 }
171171
172172 func TestUncompressedOne(t *testing.T) {
173 q := NewTargeted(Target{0.90, 0.01})
173 q := NewTargeted(map[float64]float64{0.99: 0.01})
174174 q.Insert(3.14)
175175 if g := q.Query(0.90); g != 3.14 {
176176 t.Error("want PI, got", g)
178178 }
179179
180180 func TestDefaults(t *testing.T) {
181 if g := NewTargeted(Target{0.99, 0.001}).Query(0.99); g != 0 {
181 if g := NewTargeted(map[float64]float64{0.99: 0.001}).Query(0.99); g != 0 {
182182 t.Errorf("want 0, got %f", g)
183183 }
184184 }