]> Cypherpunks.ru repositories - gostls13.git/blob - src/internal/trace/gc.go
3bd284e200fa2b0290ad854c9684667c8c554039
[gostls13.git] / src / internal / trace / gc.go
1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package trace
6
7 import (
8         "container/heap"
9         "math"
10         "sort"
11         "strings"
12         "time"
13 )
14
15 // MutatorUtil is a change in mutator utilization at a particular
16 // time. Mutator utilization functions are represented as a
17 // time-ordered []MutatorUtil.
18 type MutatorUtil struct {
19         Time int64
20         // Util is the mean mutator utilization starting at Time. This
21         // is in the range [0, 1].
22         Util float64
23 }
24
25 // UtilFlags controls the behavior of MutatorUtilization.
26 type UtilFlags int
27
28 const (
29         // UtilSTW means utilization should account for STW events.
30         // This includes non-GC STW events, which are typically user-requested.
31         UtilSTW UtilFlags = 1 << iota
32         // UtilBackground means utilization should account for
33         // background mark workers.
34         UtilBackground
35         // UtilAssist means utilization should account for mark
36         // assists.
37         UtilAssist
38         // UtilSweep means utilization should account for sweeping.
39         UtilSweep
40
41         // UtilPerProc means each P should be given a separate
42         // utilization function. Otherwise, there is a single function
43         // and each P is given a fraction of the utilization.
44         UtilPerProc
45 )
46
47 // MutatorUtilization returns a set of mutator utilization functions
48 // for the given trace. Each function will always end with 0
49 // utilization. The bounds of each function are implicit in the first
50 // and last event; outside of these bounds each function is undefined.
51 //
52 // If the UtilPerProc flag is not given, this always returns a single
53 // utilization function. Otherwise, it returns one function per P.
54 func MutatorUtilization(events []*Event, flags UtilFlags) [][]MutatorUtil {
55         if len(events) == 0 {
56                 return nil
57         }
58
59         type perP struct {
60                 // gc > 0 indicates that GC is active on this P.
61                 gc int
62                 // series the logical series number for this P. This
63                 // is necessary because Ps may be removed and then
64                 // re-added, and then the new P needs a new series.
65                 series int
66         }
67         ps := []perP{}
68         stw := 0
69
70         out := [][]MutatorUtil{}
71         assists := map[uint64]bool{}
72         block := map[uint64]*Event{}
73         bgMark := map[uint64]bool{}
74
75         for _, ev := range events {
76                 switch ev.Type {
77                 case EvGomaxprocs:
78                         gomaxprocs := int(ev.Args[0])
79                         if len(ps) > gomaxprocs {
80                                 if flags&UtilPerProc != 0 {
81                                         // End each P's series.
82                                         for _, p := range ps[gomaxprocs:] {
83                                                 out[p.series] = addUtil(out[p.series], MutatorUtil{ev.Ts, 0})
84                                         }
85                                 }
86                                 ps = ps[:gomaxprocs]
87                         }
88                         for len(ps) < gomaxprocs {
89                                 // Start new P's series.
90                                 series := 0
91                                 if flags&UtilPerProc != 0 || len(out) == 0 {
92                                         series = len(out)
93                                         out = append(out, []MutatorUtil{{ev.Ts, 1}})
94                                 }
95                                 ps = append(ps, perP{series: series})
96                         }
97                 case EvSTWStart:
98                         if flags&UtilSTW != 0 {
99                                 stw++
100                         }
101                 case EvSTWDone:
102                         if flags&UtilSTW != 0 {
103                                 stw--
104                         }
105                 case EvGCMarkAssistStart:
106                         if flags&UtilAssist != 0 {
107                                 ps[ev.P].gc++
108                                 assists[ev.G] = true
109                         }
110                 case EvGCMarkAssistDone:
111                         if flags&UtilAssist != 0 {
112                                 ps[ev.P].gc--
113                                 delete(assists, ev.G)
114                         }
115                 case EvGCSweepStart:
116                         if flags&UtilSweep != 0 {
117                                 ps[ev.P].gc++
118                         }
119                 case EvGCSweepDone:
120                         if flags&UtilSweep != 0 {
121                                 ps[ev.P].gc--
122                         }
123                 case EvGoStartLabel:
124                         if flags&UtilBackground != 0 && strings.HasPrefix(ev.SArgs[0], "GC ") && ev.SArgs[0] != "GC (idle)" {
125                                 // Background mark worker.
126                                 //
127                                 // If we're in per-proc mode, we don't
128                                 // count dedicated workers because
129                                 // they kick all of the goroutines off
130                                 // that P, so don't directly
131                                 // contribute to goroutine latency.
132                                 if !(flags&UtilPerProc != 0 && ev.SArgs[0] == "GC (dedicated)") {
133                                         bgMark[ev.G] = true
134                                         ps[ev.P].gc++
135                                 }
136                         }
137                         fallthrough
138                 case EvGoStart:
139                         if assists[ev.G] {
140                                 // Unblocked during assist.
141                                 ps[ev.P].gc++
142                         }
143                         block[ev.G] = ev.Link
144                 default:
145                         if ev != block[ev.G] {
146                                 continue
147                         }
148
149                         if assists[ev.G] {
150                                 // Blocked during assist.
151                                 ps[ev.P].gc--
152                         }
153                         if bgMark[ev.G] {
154                                 // Background mark worker done.
155                                 ps[ev.P].gc--
156                                 delete(bgMark, ev.G)
157                         }
158                         delete(block, ev.G)
159                 }
160
161                 if flags&UtilPerProc == 0 {
162                         // Compute the current average utilization.
163                         if len(ps) == 0 {
164                                 continue
165                         }
166                         gcPs := 0
167                         if stw > 0 {
168                                 gcPs = len(ps)
169                         } else {
170                                 for i := range ps {
171                                         if ps[i].gc > 0 {
172                                                 gcPs++
173                                         }
174                                 }
175                         }
176                         mu := MutatorUtil{ev.Ts, 1 - float64(gcPs)/float64(len(ps))}
177
178                         // Record the utilization change. (Since
179                         // len(ps) == len(out), we know len(out) > 0.)
180                         out[0] = addUtil(out[0], mu)
181                 } else {
182                         // Check for per-P utilization changes.
183                         for i := range ps {
184                                 p := &ps[i]
185                                 util := 1.0
186                                 if stw > 0 || p.gc > 0 {
187                                         util = 0.0
188                                 }
189                                 out[p.series] = addUtil(out[p.series], MutatorUtil{ev.Ts, util})
190                         }
191                 }
192         }
193
194         // Add final 0 utilization event to any remaining series. This
195         // is important to mark the end of the trace. The exact value
196         // shouldn't matter since no window should extend beyond this,
197         // but using 0 is symmetric with the start of the trace.
198         mu := MutatorUtil{events[len(events)-1].Ts, 0}
199         for i := range ps {
200                 out[ps[i].series] = addUtil(out[ps[i].series], mu)
201         }
202         return out
203 }
204
205 func addUtil(util []MutatorUtil, mu MutatorUtil) []MutatorUtil {
206         if len(util) > 0 {
207                 if mu.Util == util[len(util)-1].Util {
208                         // No change.
209                         return util
210                 }
211                 if mu.Time == util[len(util)-1].Time {
212                         // Take the lowest utilization at a time stamp.
213                         if mu.Util < util[len(util)-1].Util {
214                                 util[len(util)-1] = mu
215                         }
216                         return util
217                 }
218         }
219         return append(util, mu)
220 }
221
222 // totalUtil is total utilization, measured in nanoseconds. This is a
223 // separate type primarily to distinguish it from mean utilization,
224 // which is also a float64.
225 type totalUtil float64
226
227 func totalUtilOf(meanUtil float64, dur int64) totalUtil {
228         return totalUtil(meanUtil * float64(dur))
229 }
230
231 // mean returns the mean utilization over dur.
232 func (u totalUtil) mean(dur time.Duration) float64 {
233         return float64(u) / float64(dur)
234 }
235
236 // An MMUCurve is the minimum mutator utilization curve across
237 // multiple window sizes.
238 type MMUCurve struct {
239         series []mmuSeries
240 }
241
242 type mmuSeries struct {
243         util []MutatorUtil
244         // sums[j] is the cumulative sum of util[:j].
245         sums []totalUtil
246         // bands summarizes util in non-overlapping bands of duration
247         // bandDur.
248         bands []mmuBand
249         // bandDur is the duration of each band.
250         bandDur int64
251 }
252
253 type mmuBand struct {
254         // minUtil is the minimum instantaneous mutator utilization in
255         // this band.
256         minUtil float64
257         // cumUtil is the cumulative total mutator utilization between
258         // time 0 and the left edge of this band.
259         cumUtil totalUtil
260
261         // integrator is the integrator for the left edge of this
262         // band.
263         integrator integrator
264 }
265
266 // NewMMUCurve returns an MMU curve for the given mutator utilization
267 // function.
268 func NewMMUCurve(utils [][]MutatorUtil) *MMUCurve {
269         series := make([]mmuSeries, len(utils))
270         for i, util := range utils {
271                 series[i] = newMMUSeries(util)
272         }
273         return &MMUCurve{series}
274 }
275
276 // bandsPerSeries is the number of bands to divide each series into.
277 // This is only changed by tests.
278 var bandsPerSeries = 1000
279
280 func newMMUSeries(util []MutatorUtil) mmuSeries {
281         // Compute cumulative sum.
282         sums := make([]totalUtil, len(util))
283         var prev MutatorUtil
284         var sum totalUtil
285         for j, u := range util {
286                 sum += totalUtilOf(prev.Util, u.Time-prev.Time)
287                 sums[j] = sum
288                 prev = u
289         }
290
291         // Divide the utilization curve up into equal size
292         // non-overlapping "bands" and compute a summary for each of
293         // these bands.
294         //
295         // Compute the duration of each band.
296         numBands := bandsPerSeries
297         if numBands > len(util) {
298                 // There's no point in having lots of bands if there
299                 // aren't many events.
300                 numBands = len(util)
301         }
302         dur := util[len(util)-1].Time - util[0].Time
303         bandDur := (dur + int64(numBands) - 1) / int64(numBands)
304         if bandDur < 1 {
305                 bandDur = 1
306         }
307         // Compute the bands. There are numBands+1 bands in order to
308         // record the final cumulative sum.
309         bands := make([]mmuBand, numBands+1)
310         s := mmuSeries{util, sums, bands, bandDur}
311         leftSum := integrator{&s, 0}
312         for i := range bands {
313                 startTime, endTime := s.bandTime(i)
314                 cumUtil := leftSum.advance(startTime)
315                 predIdx := leftSum.pos
316                 minUtil := 1.0
317                 for i := predIdx; i < len(util) && util[i].Time < endTime; i++ {
318                         minUtil = math.Min(minUtil, util[i].Util)
319                 }
320                 bands[i] = mmuBand{minUtil, cumUtil, leftSum}
321         }
322
323         return s
324 }
325
326 func (s *mmuSeries) bandTime(i int) (start, end int64) {
327         start = int64(i)*s.bandDur + s.util[0].Time
328         end = start + s.bandDur
329         return
330 }
331
332 type bandUtil struct {
333         // Utilization series index
334         series int
335         // Band index
336         i int
337         // Lower bound of mutator utilization for all windows
338         // with a left edge in this band.
339         utilBound float64
340 }
341
342 type bandUtilHeap []bandUtil
343
344 func (h bandUtilHeap) Len() int {
345         return len(h)
346 }
347
348 func (h bandUtilHeap) Less(i, j int) bool {
349         return h[i].utilBound < h[j].utilBound
350 }
351
352 func (h bandUtilHeap) Swap(i, j int) {
353         h[i], h[j] = h[j], h[i]
354 }
355
356 func (h *bandUtilHeap) Push(x any) {
357         *h = append(*h, x.(bandUtil))
358 }
359
360 func (h *bandUtilHeap) Pop() any {
361         x := (*h)[len(*h)-1]
362         *h = (*h)[:len(*h)-1]
363         return x
364 }
365
366 // UtilWindow is a specific window at Time.
367 type UtilWindow struct {
368         Time int64
369         // MutatorUtil is the mean mutator utilization in this window.
370         MutatorUtil float64
371 }
372
373 type utilHeap []UtilWindow
374
375 func (h utilHeap) Len() int {
376         return len(h)
377 }
378
379 func (h utilHeap) Less(i, j int) bool {
380         if h[i].MutatorUtil != h[j].MutatorUtil {
381                 return h[i].MutatorUtil > h[j].MutatorUtil
382         }
383         return h[i].Time > h[j].Time
384 }
385
386 func (h utilHeap) Swap(i, j int) {
387         h[i], h[j] = h[j], h[i]
388 }
389
390 func (h *utilHeap) Push(x any) {
391         *h = append(*h, x.(UtilWindow))
392 }
393
394 func (h *utilHeap) Pop() any {
395         x := (*h)[len(*h)-1]
396         *h = (*h)[:len(*h)-1]
397         return x
398 }
399
400 // An accumulator takes a windowed mutator utilization function and
401 // tracks various statistics for that function.
402 type accumulator struct {
403         mmu float64
404
405         // bound is the mutator utilization bound where adding any
406         // mutator utilization above this bound cannot affect the
407         // accumulated statistics.
408         bound float64
409
410         // Worst N window tracking
411         nWorst int
412         wHeap  utilHeap
413
414         // Mutator utilization distribution tracking
415         mud *mud
416         // preciseMass is the distribution mass that must be precise
417         // before accumulation is stopped.
418         preciseMass float64
419         // lastTime and lastMU are the previous point added to the
420         // windowed mutator utilization function.
421         lastTime int64
422         lastMU   float64
423 }
424
425 // resetTime declares a discontinuity in the windowed mutator
426 // utilization function by resetting the current time.
427 func (acc *accumulator) resetTime() {
428         // This only matters for distribution collection, since that's
429         // the only thing that depends on the progression of the
430         // windowed mutator utilization function.
431         acc.lastTime = math.MaxInt64
432 }
433
434 // addMU adds a point to the windowed mutator utilization function at
435 // (time, mu). This must be called for monotonically increasing values
436 // of time.
437 //
438 // It returns true if further calls to addMU would be pointless.
439 func (acc *accumulator) addMU(time int64, mu float64, window time.Duration) bool {
440         if mu < acc.mmu {
441                 acc.mmu = mu
442         }
443         acc.bound = acc.mmu
444
445         if acc.nWorst == 0 {
446                 // If the minimum has reached zero, it can't go any
447                 // lower, so we can stop early.
448                 return mu == 0
449         }
450
451         // Consider adding this window to the n worst.
452         if len(acc.wHeap) < acc.nWorst || mu < acc.wHeap[0].MutatorUtil {
453                 // This window is lower than the K'th worst window.
454                 //
455                 // Check if there's any overlapping window
456                 // already in the heap and keep whichever is
457                 // worse.
458                 for i, ui := range acc.wHeap {
459                         if time+int64(window) > ui.Time && ui.Time+int64(window) > time {
460                                 if ui.MutatorUtil <= mu {
461                                         // Keep the first window.
462                                         goto keep
463                                 } else {
464                                         // Replace it with this window.
465                                         heap.Remove(&acc.wHeap, i)
466                                         break
467                                 }
468                         }
469                 }
470
471                 heap.Push(&acc.wHeap, UtilWindow{time, mu})
472                 if len(acc.wHeap) > acc.nWorst {
473                         heap.Pop(&acc.wHeap)
474                 }
475         keep:
476         }
477
478         if len(acc.wHeap) < acc.nWorst {
479                 // We don't have N windows yet, so keep accumulating.
480                 acc.bound = 1.0
481         } else {
482                 // Anything above the least worst window has no effect.
483                 acc.bound = math.Max(acc.bound, acc.wHeap[0].MutatorUtil)
484         }
485
486         if acc.mud != nil {
487                 if acc.lastTime != math.MaxInt64 {
488                         // Update distribution.
489                         acc.mud.add(acc.lastMU, mu, float64(time-acc.lastTime))
490                 }
491                 acc.lastTime, acc.lastMU = time, mu
492                 if _, mudBound, ok := acc.mud.approxInvCumulativeSum(); ok {
493                         acc.bound = math.Max(acc.bound, mudBound)
494                 } else {
495                         // We haven't accumulated enough total precise
496                         // mass yet to even reach our goal, so keep
497                         // accumulating.
498                         acc.bound = 1
499                 }
500                 // It's not worth checking percentiles every time, so
501                 // just keep accumulating this band.
502                 return false
503         }
504
505         // If we've found enough 0 utilizations, we can stop immediately.
506         return len(acc.wHeap) == acc.nWorst && acc.wHeap[0].MutatorUtil == 0
507 }
508
509 // MMU returns the minimum mutator utilization for the given time
510 // window. This is the minimum utilization for all windows of this
511 // duration across the execution. The returned value is in the range
512 // [0, 1].
513 func (c *MMUCurve) MMU(window time.Duration) (mmu float64) {
514         acc := accumulator{mmu: 1.0, bound: 1.0}
515         c.mmu(window, &acc)
516         return acc.mmu
517 }
518
519 // Examples returns n specific examples of the lowest mutator
520 // utilization for the given window size. The returned windows will be
521 // disjoint (otherwise there would be a huge number of
522 // mostly-overlapping windows at the single lowest point). There are
523 // no guarantees on which set of disjoint windows this returns.
524 func (c *MMUCurve) Examples(window time.Duration, n int) (worst []UtilWindow) {
525         acc := accumulator{mmu: 1.0, bound: 1.0, nWorst: n}
526         c.mmu(window, &acc)
527         sort.Sort(sort.Reverse(acc.wHeap))
528         return ([]UtilWindow)(acc.wHeap)
529 }
530
531 // MUD returns mutator utilization distribution quantiles for the
532 // given window size.
533 //
534 // The mutator utilization distribution is the distribution of mean
535 // mutator utilization across all windows of the given window size in
536 // the trace.
537 //
538 // The minimum mutator utilization is the minimum (0th percentile) of
539 // this distribution. (However, if only the minimum is desired, it's
540 // more efficient to use the MMU method.)
541 func (c *MMUCurve) MUD(window time.Duration, quantiles []float64) []float64 {
542         if len(quantiles) == 0 {
543                 return []float64{}
544         }
545
546         // Each unrefined band contributes a known total mass to the
547         // distribution (bandDur except at the end), but in an unknown
548         // way. However, we know that all the mass it contributes must
549         // be at or above its worst-case mean mutator utilization.
550         //
551         // Hence, we refine bands until the highest desired
552         // distribution quantile is less than the next worst-case mean
553         // mutator utilization. At this point, all further
554         // contributions to the distribution must be beyond the
555         // desired quantile and hence cannot affect it.
556         //
557         // First, find the highest desired distribution quantile.
558         maxQ := quantiles[0]
559         for _, q := range quantiles {
560                 if q > maxQ {
561                         maxQ = q
562                 }
563         }
564         // The distribution's mass is in units of time (it's not
565         // normalized because this would make it more annoying to
566         // account for future contributions of unrefined bands). The
567         // total final mass will be the duration of the trace itself
568         // minus the window size. Using this, we can compute the mass
569         // corresponding to quantile maxQ.
570         var duration int64
571         for _, s := range c.series {
572                 duration1 := s.util[len(s.util)-1].Time - s.util[0].Time
573                 if duration1 >= int64(window) {
574                         duration += duration1 - int64(window)
575                 }
576         }
577         qMass := float64(duration) * maxQ
578
579         // Accumulate the MUD until we have precise information for
580         // everything to the left of qMass.
581         acc := accumulator{mmu: 1.0, bound: 1.0, preciseMass: qMass, mud: new(mud)}
582         acc.mud.setTrackMass(qMass)
583         c.mmu(window, &acc)
584
585         // Evaluate the quantiles on the accumulated MUD.
586         out := make([]float64, len(quantiles))
587         for i := range out {
588                 mu, _ := acc.mud.invCumulativeSum(float64(duration) * quantiles[i])
589                 if math.IsNaN(mu) {
590                         // There are a few legitimate ways this can
591                         // happen:
592                         //
593                         // 1. If the window is the full trace
594                         // duration, then the windowed MU function is
595                         // only defined at a single point, so the MU
596                         // distribution is not well-defined.
597                         //
598                         // 2. If there are no events, then the MU
599                         // distribution has no mass.
600                         //
601                         // Either way, all of the quantiles will have
602                         // converged toward the MMU at this point.
603                         mu = acc.mmu
604                 }
605                 out[i] = mu
606         }
607         return out
608 }
609
610 func (c *MMUCurve) mmu(window time.Duration, acc *accumulator) {
611         if window <= 0 {
612                 acc.mmu = 0
613                 return
614         }
615
616         var bandU bandUtilHeap
617         windows := make([]time.Duration, len(c.series))
618         for i, s := range c.series {
619                 windows[i] = window
620                 if max := time.Duration(s.util[len(s.util)-1].Time - s.util[0].Time); window > max {
621                         windows[i] = max
622                 }
623
624                 bandU1 := bandUtilHeap(s.mkBandUtil(i, windows[i]))
625                 if bandU == nil {
626                         bandU = bandU1
627                 } else {
628                         bandU = append(bandU, bandU1...)
629                 }
630         }
631
632         // Process bands from lowest utilization bound to highest.
633         heap.Init(&bandU)
634
635         // Refine each band into a precise window and MMU until
636         // refining the next lowest band can no longer affect the MMU
637         // or windows.
638         for len(bandU) > 0 && bandU[0].utilBound < acc.bound {
639                 i := bandU[0].series
640                 c.series[i].bandMMU(bandU[0].i, windows[i], acc)
641                 heap.Pop(&bandU)
642         }
643 }
644
645 func (c *mmuSeries) mkBandUtil(series int, window time.Duration) []bandUtil {
646         // For each band, compute the worst-possible total mutator
647         // utilization for all windows that start in that band.
648
649         // minBands is the minimum number of bands a window can span
650         // and maxBands is the maximum number of bands a window can
651         // span in any alignment.
652         minBands := int((int64(window) + c.bandDur - 1) / c.bandDur)
653         maxBands := int((int64(window) + 2*(c.bandDur-1)) / c.bandDur)
654         if window > 1 && maxBands < 2 {
655                 panic("maxBands < 2")
656         }
657         tailDur := int64(window) % c.bandDur
658         nUtil := len(c.bands) - maxBands + 1
659         if nUtil < 0 {
660                 nUtil = 0
661         }
662         bandU := make([]bandUtil, nUtil)
663         for i := range bandU {
664                 // To compute the worst-case MU, we assume the minimum
665                 // for any bands that are only partially overlapped by
666                 // some window and the mean for any bands that are
667                 // completely covered by all windows.
668                 var util totalUtil
669
670                 // Find the lowest and second lowest of the partial
671                 // bands.
672                 l := c.bands[i].minUtil
673                 r1 := c.bands[i+minBands-1].minUtil
674                 r2 := c.bands[i+maxBands-1].minUtil
675                 minBand := math.Min(l, math.Min(r1, r2))
676                 // Assume the worst window maximally overlaps the
677                 // worst minimum and then the rest overlaps the second
678                 // worst minimum.
679                 if minBands == 1 {
680                         util += totalUtilOf(minBand, int64(window))
681                 } else {
682                         util += totalUtilOf(minBand, c.bandDur)
683                         midBand := 0.0
684                         switch {
685                         case minBand == l:
686                                 midBand = math.Min(r1, r2)
687                         case minBand == r1:
688                                 midBand = math.Min(l, r2)
689                         case minBand == r2:
690                                 midBand = math.Min(l, r1)
691                         }
692                         util += totalUtilOf(midBand, tailDur)
693                 }
694
695                 // Add the total mean MU of bands that are completely
696                 // overlapped by all windows.
697                 if minBands > 2 {
698                         util += c.bands[i+minBands-1].cumUtil - c.bands[i+1].cumUtil
699                 }
700
701                 bandU[i] = bandUtil{series, i, util.mean(window)}
702         }
703
704         return bandU
705 }
706
707 // bandMMU computes the precise minimum mutator utilization for
708 // windows with a left edge in band bandIdx.
709 func (c *mmuSeries) bandMMU(bandIdx int, window time.Duration, acc *accumulator) {
710         util := c.util
711
712         // We think of the mutator utilization over time as the
713         // box-filtered utilization function, which we call the
714         // "windowed mutator utilization function". The resulting
715         // function is continuous and piecewise linear (unless
716         // window==0, which we handle elsewhere), where the boundaries
717         // between segments occur when either edge of the window
718         // encounters a change in the instantaneous mutator
719         // utilization function. Hence, the minimum of this function
720         // will always occur when one of the edges of the window
721         // aligns with a utilization change, so these are the only
722         // points we need to consider.
723         //
724         // We compute the mutator utilization function incrementally
725         // by tracking the integral from t=0 to the left edge of the
726         // window and to the right edge of the window.
727         left := c.bands[bandIdx].integrator
728         right := left
729         time, endTime := c.bandTime(bandIdx)
730         if utilEnd := util[len(util)-1].Time - int64(window); utilEnd < endTime {
731                 endTime = utilEnd
732         }
733         acc.resetTime()
734         for {
735                 // Advance edges to time and time+window.
736                 mu := (right.advance(time+int64(window)) - left.advance(time)).mean(window)
737                 if acc.addMU(time, mu, window) {
738                         break
739                 }
740                 if time == endTime {
741                         break
742                 }
743
744                 // The maximum slope of the windowed mutator
745                 // utilization function is 1/window, so we can always
746                 // advance the time by at least (mu - mmu) * window
747                 // without dropping below mmu.
748                 minTime := time + int64((mu-acc.bound)*float64(window))
749
750                 // Advance the window to the next time where either
751                 // the left or right edge of the window encounters a
752                 // change in the utilization curve.
753                 if t1, t2 := left.next(time), right.next(time+int64(window))-int64(window); t1 < t2 {
754                         time = t1
755                 } else {
756                         time = t2
757                 }
758                 if time < minTime {
759                         time = minTime
760                 }
761                 if time >= endTime {
762                         // For MMUs we could stop here, but for MUDs
763                         // it's important that we span the entire
764                         // band.
765                         time = endTime
766                 }
767         }
768 }
769
770 // An integrator tracks a position in a utilization function and
771 // integrates it.
772 type integrator struct {
773         u *mmuSeries
774         // pos is the index in u.util of the current time's non-strict
775         // predecessor.
776         pos int
777 }
778
779 // advance returns the integral of the utilization function from 0 to
780 // time. advance must be called on monotonically increasing values of
781 // times.
782 func (in *integrator) advance(time int64) totalUtil {
783         util, pos := in.u.util, in.pos
784         // Advance pos until pos+1 is time's strict successor (making
785         // pos time's non-strict predecessor).
786         //
787         // Very often, this will be nearby, so we optimize that case,
788         // but it may be arbitrarily far away, so we handled that
789         // efficiently, too.
790         const maxSeq = 8
791         if pos+maxSeq < len(util) && util[pos+maxSeq].Time > time {
792                 // Nearby. Use a linear scan.
793                 for pos+1 < len(util) && util[pos+1].Time <= time {
794                         pos++
795                 }
796         } else {
797                 // Far. Binary search for time's strict successor.
798                 l, r := pos, len(util)
799                 for l < r {
800                         h := int(uint(l+r) >> 1)
801                         if util[h].Time <= time {
802                                 l = h + 1
803                         } else {
804                                 r = h
805                         }
806                 }
807                 pos = l - 1 // Non-strict predecessor.
808         }
809         in.pos = pos
810         var partial totalUtil
811         if time != util[pos].Time {
812                 partial = totalUtilOf(util[pos].Util, time-util[pos].Time)
813         }
814         return in.u.sums[pos] + partial
815 }
816
817 // next returns the smallest time t' > time of a change in the
818 // utilization function.
819 func (in *integrator) next(time int64) int64 {
820         for _, u := range in.u.util[in.pos:] {
821                 if u.Time > time {
822                         return u.Time
823                 }
824         }
825         return 1<<63 - 1
826 }