Source file
src/runtime/mgclimit_test.go
Documentation: runtime
1
2
3
4
5 package runtime_test
6
7 import (
8 . "runtime"
9 "testing"
10 "time"
11 )
12
13 func TestGCCPULimiter(t *testing.T) {
14 const procs = 14
15
16
17 ticks := int64(0)
18 advance := func(d time.Duration) int64 {
19 t.Helper()
20 ticks += int64(d)
21 return ticks
22 }
23
24
25
26 assistTime := func(d time.Duration, frac float64) int64 {
27 t.Helper()
28 return int64(frac * float64(d) * procs)
29 }
30
31 l := NewGCCPULimiter(ticks, procs)
32
33
34 var baseOverflow uint64
35 for i := 0; i < 2; i++ {
36 t.Logf("Iteration %d", i+1)
37
38 if l.Capacity() != procs*CapacityPerProc {
39 t.Fatalf("unexpected capacity: %d", l.Capacity())
40 }
41 if l.Fill() != 0 {
42 t.Fatalf("expected empty bucket to start")
43 }
44
45
46
47 l.Update(advance(10 * time.Millisecond))
48 l.Update(advance(1 * time.Second))
49 l.Update(advance(1 * time.Hour))
50 if l.Fill() != 0 {
51 t.Fatalf("expected empty bucket from only accumulating mutator time, got fill of %d cpu-ns", l.Fill())
52 }
53
54
55
56 if l.NeedUpdate(advance(GCCPULimiterUpdatePeriod / 2)) {
57 t.Fatal("need update even though updated half a period ago")
58 }
59 if !l.NeedUpdate(advance(GCCPULimiterUpdatePeriod)) {
60 t.Fatal("doesn't need update even though updated 1.5 periods ago")
61 }
62 l.Update(advance(0))
63 if l.NeedUpdate(advance(0)) {
64 t.Fatal("need update even though just updated")
65 }
66
67
68
69 l.StartGCTransition(true, advance(109*time.Millisecond))
70 l.FinishGCTransition(advance(2*time.Millisecond + 1*time.Microsecond))
71
72 if expect := uint64((2*time.Millisecond + 1*time.Microsecond) * procs); l.Fill() != expect {
73 t.Fatalf("expected fill of %d, got %d cpu-ns", expect, l.Fill())
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88 factor := (1 / (1 - 2*GCBackgroundUtilization))
89 fill := (2*time.Millisecond + 1*time.Microsecond) * procs
90 l.Update(advance(time.Duration(factor * float64(fill-procs) / procs)))
91 if l.Fill() != procs {
92 t.Fatalf("expected fill %d cpu-ns from draining after a GC started, got fill of %d cpu-ns", procs, l.Fill())
93 }
94
95
96 l.Update(advance(2 * procs * CapacityPerProc))
97 if l.Fill() != 0 {
98 t.Fatalf("expected empty bucket from draining, got fill of %d cpu-ns", l.Fill())
99 }
100
101
102 l.AddAssistTime(assistTime(10*time.Millisecond, 0.5-GCBackgroundUtilization))
103 l.Update(advance(10 * time.Millisecond))
104 if l.Fill() != 0 {
105 t.Fatalf("expected empty bucket from 50%% GC work, got fill of %d cpu-ns", l.Fill())
106 }
107
108
109 l.AddAssistTime(assistTime(time.Millisecond, 1.0-GCBackgroundUtilization))
110 l.Update(advance(time.Millisecond))
111 if expect := uint64(procs * time.Millisecond); l.Fill() != expect {
112 t.Errorf("expected %d fill from 100%% GC CPU, got fill of %d cpu-ns", expect, l.Fill())
113 }
114 if l.Limiting() {
115 t.Errorf("limiter is enabled after filling bucket but shouldn't be")
116 }
117 if t.Failed() {
118 t.FailNow()
119 }
120
121
122 l.AddAssistTime(assistTime(CapacityPerProc-time.Millisecond, 1.0-GCBackgroundUtilization))
123 l.Update(advance(CapacityPerProc - time.Millisecond))
124 if l.Fill() != l.Capacity() {
125 t.Errorf("expected bucket filled to capacity %d, got %d", l.Capacity(), l.Fill())
126 }
127 if !l.Limiting() {
128 t.Errorf("limiter is not enabled after filling bucket but should be")
129 }
130 if l.Overflow() != 0+baseOverflow {
131 t.Errorf("bucket filled exactly should not have overflow, found %d", l.Overflow())
132 }
133 if t.Failed() {
134 t.FailNow()
135 }
136
137
138
139 l.AddAssistTime(assistTime(1*time.Second, 0.5-GCBackgroundUtilization))
140 l.Update(advance(1 * time.Second))
141 if l.Fill() != l.Capacity() {
142 t.Errorf("expected bucket filled to capacity %d, got %d", l.Capacity(), l.Fill())
143 }
144 if !l.Limiting() {
145 t.Errorf("limiter is not enabled after filling bucket but should be")
146 }
147 if l.Overflow() != 0+baseOverflow {
148 t.Errorf("bucket filled exactly should not have overflow, found %d", l.Overflow())
149 }
150 if t.Failed() {
151 t.FailNow()
152 }
153
154
155 l.AddAssistTime(assistTime(CapacityPerProc, 0))
156 l.Update(advance(CapacityPerProc))
157 if expect := l.Capacity() / 2; l.Fill() != expect {
158 t.Errorf("failed to drain to %d, got fill %d", expect, l.Fill())
159 }
160 if l.Limiting() {
161 t.Errorf("limiter is enabled after draining bucket but shouldn't be")
162 }
163 if t.Failed() {
164 t.FailNow()
165 }
166
167
168 l.AddAssistTime(assistTime(CapacityPerProc, 1.0-GCBackgroundUtilization))
169 l.Update(advance(CapacityPerProc))
170 if l.Fill() != l.Capacity() {
171 t.Errorf("failed to fill to capacity %d, got fill %d", l.Capacity(), l.Fill())
172 }
173 if !l.Limiting() {
174 t.Errorf("limiter is not enabled after overfill but should be")
175 }
176 if expect := uint64(CapacityPerProc * procs / 2); l.Overflow() != expect+baseOverflow {
177 t.Errorf("bucket overfilled should have overflow %d, found %d", expect, l.Overflow())
178 }
179 if t.Failed() {
180 t.FailNow()
181 }
182
183
184 l.AddAssistTime(assistTime(1*time.Millisecond, 1.0-GCBackgroundUtilization))
185 l.StartGCTransition(false, advance(1*time.Millisecond))
186 if l.Fill() != l.Capacity() {
187 t.Errorf("failed to maintain fill to capacity %d, got fill %d", l.Capacity(), l.Fill())
188 }
189 if !l.Limiting() {
190 t.Errorf("limiter is not enabled after overfill but should be")
191 }
192 if expect := uint64((CapacityPerProc/2 + time.Millisecond) * procs); l.Overflow() != expect+baseOverflow {
193 t.Errorf("bucket overfilled should have overflow %d, found %d", expect, l.Overflow())
194 }
195 if t.Failed() {
196 t.FailNow()
197 }
198
199
200 l.FinishGCTransition(advance(5 * time.Millisecond))
201 if l.Fill() != l.Capacity() {
202 t.Errorf("failed to maintain fill to capacity %d, got fill %d", l.Capacity(), l.Fill())
203 }
204 if !l.Limiting() {
205 t.Errorf("limiter is not enabled after overfill but should be")
206 }
207 if expect := uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs); l.Overflow() != expect+baseOverflow {
208 t.Errorf("bucket overfilled should have overflow %d, found %d", expect, l.Overflow())
209 }
210 if t.Failed() {
211 t.FailNow()
212 }
213
214
215 expectFill := l.Capacity()
216 l.ResetCapacity(advance(0), procs+10)
217 if l.Fill() != expectFill {
218 t.Errorf("failed to maintain fill at old capacity %d, got fill %d", expectFill, l.Fill())
219 }
220 if l.Limiting() {
221 t.Errorf("limiter is enabled after resetting capacity higher")
222 }
223 if expect := uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs); l.Overflow() != expect+baseOverflow {
224 t.Errorf("bucket overflow %d should have remained constant, found %d", expect, l.Overflow())
225 }
226 if t.Failed() {
227 t.FailNow()
228 }
229
230
231
232
233
234
235 l.ResetCapacity(advance(0), procs-10)
236 if l.Fill() != l.Capacity() {
237 t.Errorf("failed lower fill to new capacity %d, got fill %d", l.Capacity(), l.Fill())
238 }
239 if !l.Limiting() {
240 t.Errorf("limiter is disabled after resetting capacity lower")
241 }
242 if expect := uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs); l.Overflow() != expect+baseOverflow {
243 t.Errorf("bucket overflow %d should have remained constant, found %d", expect, l.Overflow())
244 }
245 if t.Failed() {
246 t.FailNow()
247 }
248
249
250 l.ResetCapacity(advance(CapacityPerProc*procs), procs)
251
252
253 baseOverflow += uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs)
254 }
255 }
256
View as plain text