github.com/grafana/pyroscope@v1.18.0/pkg/segmentwriter/client/distributor/placement/adaptiveplacement/ewma/ewma_test.go (about) 1 package ewma 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/stretchr/testify/assert" 8 ) 9 10 func Test_Rate_HalfLife(t *testing.T) { 11 s := int64(0) 12 r := NewHalfLife(time.Second * 10) 13 14 // Expected rate 100. 15 step := int64(1e9 / 10) // 100ms 16 for i := 0; i < 500; i++ { // 50s. 17 r.update(10, s) 18 if i == 100 { // 10s (half-life) 19 // Half-life: 10s => 100 * 0.5 = 50. 20 assert.InEpsilon(t, float64(50), r.value(s), 0.0001) 21 } 22 s += step 23 } 24 // Here and below: Value takes into account the time 25 // since the last update, so we need to adjust it to 26 // compensate the last iteration. 27 assert.InEpsilon(t, 100, r.value(s-step), 0.05) 28 29 // Rate decreases. 30 step = int64(1e9 / 10) // 100ms 31 for i := 0; i < 500; i++ { // 50s. 32 r.update(5, s) 33 s += step 34 } 35 assert.InEpsilon(t, 50, r.value(s-step), 0.05) 36 37 // Exactly 1s rate. 38 step = int64(1e9) // 1s 39 for i := 0; i < 50; i++ { // 50s 40 r.update(50, s) 41 s += step 42 } 43 assert.InEpsilon(t, 50, r.value(s-step), 0.005) 44 45 // Sub-second rate. 46 step = int64(1e9 * 2) // 2s 47 for i := 0; i < 50; i++ { // 50s. 48 r.update(1, s) 49 if i == 5 { // 10s (half-life) 50 // We expect that after expiration of the half-life interval, 51 // the rate should be roughly 25: (~50 + 0.5) / 2 = ~25. 52 // The numbers are not exact because r has state: 53 // in the beginning it is slightly greater than 50. 54 assert.InEpsilon(t, 25, int(r.value(s)), 0.0001) 55 } 56 s += step // Once per two seconds. 57 } 58 assert.InEpsilon(t, 0.5, r.value(s-step), 0.5) 59 } 60 61 func Test_Rate_HalfLife_Tail(t *testing.T) { 62 // Expected rate 100. 63 const ( 64 step int64 = 1e9 / 10 // 100ms 65 steps int64 = 1200 // 120s. 66 update int64 = 10 67 68 rate = update * int64(time.Second) / step 69 halflife = time.Second * 10 70 ) 71 72 r := NewHalfLife(halflife) 73 var s int64 74 for i := int64(0); i < steps; i++ { 75 r.update(float64(update), s) 76 s += step 77 if i == int64(halflife)/step { 78 // Just in case: check half-life value. 79 assert.InEpsilon(t, float64(rate/2), r.value(s), 0.0001) 80 } 81 } 82 83 assert.InEpsilon(t, float64(rate), r.value(s), 0.05) 84 // Now we stop updating the rate and 85 // expect that it will decay to zero. 86 timespan := s + (steps * step) 87 assert.Less(t, r.value(timespan), float64(1)) 88 // Half-life check: note that r is not exactly 100, 89 // therefore we will have some error here. 90 timespan = s + int64(halflife) 91 assert.InEpsilon(t, r.value(timespan), r.value(s)/2, 0.05) 92 } 93 94 func Test_Rate_HalfLife_Complement(t *testing.T) { 95 // The test examines the complementarity of two rates, 96 // when the sum of the rates is constant. 97 const ( 98 step = int64(1e9 / 10) // 100ms 99 steps = 600 // 60s. 100 update = 10 101 102 rate = update * int64(time.Second) / step 103 halflife = time.Second * 10 104 ) 105 106 s := int64(0) 107 a := NewHalfLife(halflife) 108 for i := 0; i < steps; i++ { 109 a.update(update, s) 110 s += step 111 } 112 assert.InEpsilon(t, float64(rate), a.value(s), 0.05) 113 114 b := NewHalfLife(halflife) 115 const interval = steps / 10 116 for n := 0; n < steps; n += interval { 117 for i := 0; i < interval; i++ { 118 b.update(update, s) 119 s += step 120 } 121 // The sum of the rates is expected to be constant. 122 assert.InEpsilon(t, float64(rate), a.value(s)+b.value(s), 0.05) 123 } 124 } 125 126 func Test_Rate_Lifetime(t *testing.T) { 127 // Expected rate 100. 128 const ( 129 step int64 = 1e9 / 10 // 100ms 130 steps int64 = 100 // 10s. 131 update int64 = 10 132 133 rate = update * int64(time.Second) / step 134 // lifetime/3 approximates SMA (error is ~5%). 135 lifetime = time.Second * 10 / 3 136 ) 137 138 r := New(lifetime) 139 var s int64 140 for i := int64(0); i < steps; i++ { 141 r.update(float64(update), s) 142 s += step 143 } 144 145 assert.InEpsilon(t, float64(rate), r.value(s), 0.05) 146 // Now we stop updating the rate and expect 147 // that it will decay to zero. Note that the 148 // value decays more slowly: after 20 seconds 149 // we still observe a non-zero value (~5%). 150 for i := int64(0); i < 2*steps; i++ { 151 s += step 152 } 153 assert.Less(t, r.value(s), float64(5)) 154 }