quantile: Replace container/list with a slice
Better data locality trumps asymptotic behavior in this case.
benchmark old ns/op new ns/op delta
BenchmarkQuerySmallEpsilon 44491 6782 -84.76%
BenchmarkInsertBiasedSmallEpsilon 2641 871 -67.02%
BenchmarkQuery 691 306 -55.72%
BenchmarkInsertBiased 324 177 -45.37%
BenchmarkInsertTargetedSmallEpsilon 1016 616 -39.37%
BenchmarkInsertTargeted 294 191 -35.03%
Caleb Spare
9 years ago
14 | 14 | package quantile |
15 | 15 | |
16 | 16 | import ( |
17 | "container/list" | |
18 | 17 | "math" |
19 | 18 | "sort" |
20 | 19 | ) |
86 | 85 | |
87 | 86 | func newStream(ƒ invariant) *Stream { |
88 | 87 | const defaultEpsilon = 0.01 |
89 | x := &stream{epsilon: defaultEpsilon, ƒ: ƒ, l: list.New()} | |
88 | x := &stream{epsilon: defaultEpsilon, ƒ: ƒ} | |
90 | 89 | return &Stream{x, make(Samples, 0, 500), true} |
91 | 90 | } |
92 | 91 | |
169 | 168 | } |
170 | 169 | |
171 | 170 | func (s *Stream) flushed() bool { |
172 | return s.stream.l.Len() > 0 | |
171 | return len(s.stream.l) > 0 | |
173 | 172 | } |
174 | 173 | |
175 | 174 | type stream struct { |
176 | 175 | epsilon float64 |
177 | 176 | n float64 |
178 | l *list.List | |
177 | l []*Sample | |
179 | 178 | ƒ invariant |
180 | 179 | } |
181 | 180 | |
188 | 187 | } |
189 | 188 | |
190 | 189 | func (s *stream) reset() { |
191 | s.l.Init() | |
190 | s.l = s.l[:0] | |
192 | 191 | s.n = 0 |
193 | 192 | } |
194 | 193 | |
208 | 207 | // NOTE: I used a goto over defer because it bought me a few extra |
209 | 208 | // nanoseconds. I know. I know. |
210 | 209 | var r float64 |
211 | e := s.l.Front() | |
210 | i := 0 | |
212 | 211 | return func(v, w float64) { |
213 | for ; e != nil; e = e.Next() { | |
214 | c := e.Value.(*Sample) | |
212 | for ; i < len(s.l); i++ { | |
213 | c := s.l[i] | |
215 | 214 | if c.Value > v { |
216 | sm := &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1} | |
217 | s.l.InsertBefore(sm, e) | |
215 | // Insert at position i. | |
216 | s.l = append(s.l, nil) | |
217 | copy(s.l[i+1:], s.l[i:]) | |
218 | s.l[i] = &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1} | |
219 | i++ | |
218 | 220 | goto inserted |
219 | 221 | } |
220 | 222 | r += c.Width |
221 | 223 | } |
222 | s.l.PushBack(&Sample{v, w, 0}) | |
224 | s.l = append(s.l, &Sample{v, w, 0}) | |
225 | i++ | |
223 | 226 | inserted: |
224 | 227 | s.n += w |
225 | 228 | } |
230 | 233 | } |
231 | 234 | |
232 | 235 | func (s *stream) query(q float64) float64 { |
233 | e := s.l.Front() | |
234 | 236 | t := math.Ceil(q * s.n) |
235 | 237 | t += math.Ceil(s.ƒ(s, t) / 2) |
236 | p := e.Value.(*Sample) | |
237 | e = e.Next() | |
238 | p := s.l[0] | |
238 | 239 | r := float64(0) |
239 | for e != nil { | |
240 | c := e.Value.(*Sample) | |
240 | for _, c := range s.l[1:] { | |
241 | 241 | if r+c.Width+c.Delta > t { |
242 | 242 | return p.Value |
243 | 243 | } |
244 | 244 | r += p.Width |
245 | 245 | p = c |
246 | e = e.Next() | |
247 | 246 | } |
248 | 247 | return p.Value |
249 | 248 | } |
250 | 249 | |
251 | 250 | func (s *stream) compress() { |
252 | if s.l.Len() < 2 { | |
251 | if len(s.l) < 2 { | |
253 | 252 | return |
254 | 253 | } |
255 | e := s.l.Back() | |
256 | x := e.Value.(*Sample) | |
254 | x := s.l[len(s.l)-1] | |
257 | 255 | r := s.n - 1 - x.Width |
258 | e = e.Prev() | |
259 | for e != nil { | |
260 | c := e.Value.(*Sample) | |
256 | ||
257 | for i := len(s.l) - 2; i >= 0; i-- { | |
258 | c := s.l[i] | |
261 | 259 | if c.Width+x.Width+x.Delta <= s.ƒ(s, r) { |
262 | 260 | x.Width += c.Width |
263 | o := e | |
264 | e = e.Prev() | |
265 | s.l.Remove(o) | |
261 | // Remove element at i. | |
262 | copy(s.l[i:], s.l[i+1:]) | |
263 | s.l[len(s.l)-1] = nil | |
264 | s.l = s.l[:len(s.l)-1] | |
266 | 265 | } else { |
267 | 266 | x = c |
268 | e = e.Prev() | |
269 | 267 | } |
270 | 268 | r -= c.Width |
271 | 269 | } |
272 | 270 | } |
273 | 271 | |
274 | 272 | func (s *stream) samples() Samples { |
275 | samples := make(Samples, 0, s.l.Len()) | |
276 | for e := s.l.Front(); e != nil; e = e.Next() { | |
277 | samples = append(samples, *e.Value.(*Sample)) | |
273 | samples := make(Samples, len(s.l)) | |
274 | for i, c := range s.l { | |
275 | samples[i] = *c | |
278 | 276 | } |
279 | 277 | return samples |
280 | 278 | } |