LCOV - code coverage report
Current view: top level - pebble/replay - sampled_metric.go (source / functions) Hit Total Coverage
Test: 2024-03-28 08:15Z b6e563f6 - tests + meta.lcov Lines: 62 73 84.9 %
Date: 2024-03-28 08:16:48 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use
       2             : // of this source code is governed by a BSD-style license that can be found in
       3             : // the LICENSE file.
       4             : 
       5             : package replay
       6             : 
       7             : import (
       8             :         "math"
       9             :         "time"
      10             : 
      11             :         "github.com/guptarohit/asciigraph"
      12             : )
      13             : 
      14             : // SampledMetric holds a metric that is sampled at various points of workload
      15             : // replay. Samples are collected when a new step in the workload is applied to
      16             : // the database, and whenever a compaction completes.
      17             : type SampledMetric struct {
      18             :         samples []sample
      19             :         first   time.Time
      20             : }
      21             : 
      22             : type sample struct {
      23             :         since time.Duration
      24             :         value int64
      25             : }
      26             : 
      27           1 : func (m *SampledMetric) record(v int64) {
      28           1 :         if m.first.IsZero() {
      29           1 :                 m.first = time.Now()
      30           1 :         }
      31           1 :         m.samples = append(m.samples, sample{
      32           1 :                 since: time.Since(m.first),
      33           1 :                 value: v,
      34           1 :         })
      35             : }
      36             : 
      37             : // Plot returns an ASCII graph plot of the metric over time, with the provided
      38             : // width and height determining the size of the graph and the number of representable discrete x and y
      39             : // points. All values are first
      40             : // multiplied by the provided scale parameter before graphing.
      41           1 : func (m *SampledMetric) Plot(width, height int, scale float64) string {
      42           1 :         values := m.Values(width)
      43           1 :         for i := range values {
      44           1 :                 values[i] *= scale
      45           1 :         }
      46           1 :         return asciigraph.Plot(values, asciigraph.Height(height))
      47             : }
      48             : 
      49             : // PlotIncreasingPerSec returns an ASCII graph plot of the increasing delta of a
      50             : // metric over time, per-second. The provided width and height determine the
      51             : // size of the graph and the number of representable discrete x and y points.
      52             : // All deltas are multiplied by the provided scale parameter and scaled to
      53             : // per-second before graphing.
      54           1 : func (m *SampledMetric) PlotIncreasingPerSec(width, height int, scale float64) string {
      55           1 :         bucketDur, values := m.values(width)
      56           1 :         deltas := make([]float64, width)
      57           1 :         for i := range values {
      58           1 :                 if i == 0 {
      59           1 :                         deltas[i] = (values[i] * scale) / bucketDur.Seconds()
      60           1 :                 } else if values[i] > values[i-1] {
      61           1 :                         deltas[i] = (values[i] - values[i-1]) * scale / bucketDur.Seconds()
      62           1 :                 }
      63             :         }
      64           1 :         return asciigraph.Plot(deltas, asciigraph.Height(height))
      65             : }
      66             : 
      67             : // Mean calculates the mean value of the metric.
      68           1 : func (m *SampledMetric) Mean() float64 {
      69           1 :         var sum float64
      70           1 :         if len(m.samples) == 0 {
      71           0 :                 return 0.0
      72           0 :         }
      73           1 :         for _, s := range m.samples {
      74           1 :                 sum += float64(s.value)
      75           1 :         }
      76           1 :         return sum / float64(len(m.samples))
      77             : }
      78             : 
      79             : // Min calculates the mininum value of the metric.
      80           0 : func (m *SampledMetric) Min() int64 {
      81           0 :         min := int64(math.MaxInt64)
      82           0 :         for _, s := range m.samples {
      83           0 :                 if min > s.value {
      84           0 :                         min = s.value
      85           0 :                 }
      86             :         }
      87           0 :         return min
      88             : }
      89             : 
      90             : // Max calculates the maximum value of the metric.
      91           1 : func (m *SampledMetric) Max() int64 {
      92           1 :         var max int64
      93           1 :         for _, s := range m.samples {
      94           1 :                 if max < s.value {
      95           1 :                         max = s.value
      96           1 :                 }
      97             :         }
      98           1 :         return max
      99             : }
     100             : 
     101             : // Values returns the values of the metric, distributed across n discrete
     102             : // buckets that are equally spaced over time. If multiple values fall within a
     103             : // bucket, the latest recorded value is used. If no values fall within a bucket,
     104             : // the next recorded value is used.
     105           1 : func (m *SampledMetric) Values(n int) []float64 {
     106           1 :         _, values := m.values(n)
     107           1 :         return values
     108           1 : }
     109             : 
     110           1 : func (m *SampledMetric) values(buckets int) (bucketDur time.Duration, values []float64) {
     111           1 :         if len(m.samples) == 0 || buckets < 1 {
     112           0 :                 return bucketDur, nil
     113           0 :         }
     114             : 
     115           1 :         values = make([]float64, buckets)
     116           1 :         totalDur := m.samples[len(m.samples)-1].since
     117           1 :         bucketDur = totalDur / time.Duration(buckets)
     118           1 : 
     119           1 :         for i, b := 0, 0; i < len(m.samples); i++ {
     120           1 :                 // Fill any buckets that precede this value with the previous value.
     121           1 :                 bi := int(m.samples[i].since / bucketDur)
     122           1 :                 if bi == buckets {
     123           1 :                         bi = buckets - 1
     124           1 :                 }
     125           1 :                 if b < bi {
     126           1 :                         b++
     127           1 :                         for ; b < bi; b++ {
     128           1 :                                 values[b] = float64(m.samples[i].value)
     129           1 :                         }
     130             :                 }
     131           1 :                 values[bi] = float64(m.samples[i].value)
     132           1 :                 b = bi
     133             :         }
     134           1 :         return bucketDur, values
     135             : }

Generated by: LCOV version 1.14