已合并
runtime:change gcBackgroundUtilization optional #70
runtime:change gcBackgroundUtilization optional #70
已合并
rfwang07创建于 5月18日
6 个文件变更+89-45
@@ -1324,25 +1324,26 @@ func GCTestPointerClass(p unsafe.Pointer) string {
1324const Raceenabled = raceenabled1324const Raceenabled = raceenabled
1325 1325 
1326const (1326const (
1327- GCBackgroundUtilization = gcBackgroundUtilization
1328- GCGoalUtilization = gcGoalUtilization
1329 DefaultHeapMinimum = defaultHeapMinimum1327 DefaultHeapMinimum = defaultHeapMinimum
1330 MemoryLimitHeapGoalHeadroomPercent = memoryLimitHeapGoalHeadroomPercent1328 MemoryLimitHeapGoalHeadroomPercent = memoryLimitHeapGoalHeadroomPercent
1331 MemoryLimitMinHeapGoalHeadroom = memoryLimitMinHeapGoalHeadroom1329 MemoryLimitMinHeapGoalHeadroom = memoryLimitMinHeapGoalHeadroom
1332)1330)
1333 1331 
1332+var GCBackgroundUtilization = gcController.gcRatio
1333+var GCGoalUtilization = gcGoalUtilization
1334+ 
1334type GCController struct {1335type GCController struct {
1335 gcControllerState1336 gcControllerState
1336}1337}
1337 1338 
1338-func NewGCController(gcPercent int, memoryLimit int64) *GCController {1339+func NewGCController(gcPercent int, memoryLimit int64, gcRatio float64) *GCController {
1339 // Force the controller to escape. We're going to1340 // Force the controller to escape. We're going to
1340 // do 64-bit atomics on it, and if it gets stack-allocated1341 // do 64-bit atomics on it, and if it gets stack-allocated
1341 // on a 32-bit architecture, it may get allocated unaligned1342 // on a 32-bit architecture, it may get allocated unaligned
1342 // space.1343 // space.
1343 g := Escape(new(GCController))1344 g := Escape(new(GCController))
1344 g.gcControllerState.test = true // Mark it as a test copy.1345 g.gcControllerState.test = true // Mark it as a test copy.
1345- g.init(int32(gcPercent), memoryLimit)1346+ g.init(int32(gcPercent), memoryLimit, gcRatio)
1346 return g1347 return g
1347}1348}
1348 1349 
@@ -184,7 +184,7 @@ func gcinit() {
184 // Initialize GC pacer state.184 // Initialize GC pacer state.
185 // Use the environment variable GOGC for the initial gcPercent value.185 // Use the environment variable GOGC for the initial gcPercent value.
186 // Use the environment variable GOMEMLIMIT for the initial memoryLimit value.186 // Use the environment variable GOMEMLIMIT for the initial memoryLimit value.
187- gcController.init(readGOGC(), readGOMEMLIMIT())187+ gcController.init(readGOGC(), readGOMEMLIMIT(), readGOGCRATIO())
188 188 
189 work.startSema = 1189 work.startSema = 1
190 work.markDoneSema = 1190 work.markDoneSema = 1
@@ -270,12 +270,12 @@ const (
270 270 
271 // gcMarkWorkerFractionalMode indicates that a P is currently271 // gcMarkWorkerFractionalMode indicates that a P is currently
272 // running the "fractional" mark worker. The fractional worker272 // running the "fractional" mark worker. The fractional worker
273- // is necessary when GOMAXPROCS*gcBackgroundUtilization is not273+ // is necessary when GOMAXPROCS*gcController.gcRatio is not
274 // an integer and using only dedicated workers would result in274 // an integer and using only dedicated workers would result in
275- // utilization too far from the target of gcBackgroundUtilization.275+ // utilization too far from the target of gcController.gcRatio.
276 // The fractional worker should run until it is preempted and276 // The fractional worker should run until it is preempted and
277 // will be scheduled to pick up the fractional part of277 // will be scheduled to pick up the fractional part of
278- // GOMAXPROCS*gcBackgroundUtilization.278+ // GOMAXPROCS*gcController.gcRatio.
279 gcMarkWorkerFractionalMode279 gcMarkWorkerFractionalMode
280 280 
281 // gcMarkWorkerIdleMode indicates that a P is running the mark281 // gcMarkWorkerIdleMode indicates that a P is running the mark
@@ -230,7 +230,7 @@ func (l *gcCPULimiterState) updateLocked(now int64) {
230 // Compute total GC time.230 // Compute total GC time.
231 windowGCTime := assistTime231 windowGCTime := assistTime
232 if l.gcEnabled {232 if l.gcEnabled {
233- windowGCTime += int64(float64(windowTotalTime) * gcBackgroundUtilization)233+ windowGCTime += int64(float64(windowTotalTime) * gcController.gcRatio)
234 }234 }
235 235 
236 // Subtract out all idle time from the total time. Do this after computing236 // Subtract out all idle time from the total time. Do this after computing
@@ -76,13 +76,13 @@ func TestGCCPULimiter(t *testing.T) {
76 // Test passing time without assists during a GC. Specifically, just enough to drain the bucket to76 // Test passing time without assists during a GC. Specifically, just enough to drain the bucket to
77 // exactly procs nanoseconds (easier to get to because of rounding).77 // exactly procs nanoseconds (easier to get to because of rounding).
78 //78 //
79- // The window we need to drain the bucket is 1/(1-2*gcBackgroundUtilization) times the current fill:79+ // The window we need to drain the bucket is 1/(1-2*gcController.gcRatio) times the current fill:
80 //80 //
81- // fill + (window * procs * gcBackgroundUtilization - window * procs * (1-gcBackgroundUtilization)) = n81+ // fill + (window * procs * gcController.gcRatio - window * procs * (1-gcController.gcRatio)) = n
82- // fill = n - (window * procs * gcBackgroundUtilization - window * procs * (1-gcBackgroundUtilization))82+ // fill = n - (window * procs * gcController.gcRatio - window * procs * (1-gcController.gcRatio))
83- // fill = n + window * procs * ((1-gcBackgroundUtilization) - gcBackgroundUtilization)83+ // fill = n + window * procs * ((1-gcController.gcRatio) - gcController.gcRatio)
84- // fill = n + window * procs * (1-2*gcBackgroundUtilization)84+ // fill = n + window * procs * (1-2*gcController.gcRatio)
85- // window = (fill - n) / (procs * (1-2*gcBackgroundUtilization)))85+ // window = (fill - n) / (procs * (1-2*gcController.gcRatio)))
86 //86 //
87 // And here we want n=procs:87 // And here we want n=procs:
88 factor := (1 / (1 - 2*GCBackgroundUtilization))88 factor := (1 / (1 - 2*GCBackgroundUtilization))
@@ -12,30 +12,6 @@ import (
12)12)
13 13 
14const (14const (
15- // gcGoalUtilization is the goal CPU utilization for
16- // marking as a fraction of GOMAXPROCS.
17- //
18- // Increasing the goal utilization will shorten GC cycles as the GC
19- // has more resources behind it, lessening costs from the write barrier,
20- // but comes at the cost of increasing mutator latency.
21- gcGoalUtilization = gcBackgroundUtilization
22- 
23- // gcBackgroundUtilization is the fixed CPU utilization for background
24- // marking. It must be <= gcGoalUtilization. The difference between
25- // gcGoalUtilization and gcBackgroundUtilization will be made up by
26- // mark assists. The scheduler will aim to use within 50% of this
27- // goal.
28- //
29- // As a general rule, there's little reason to set gcBackgroundUtilization
30- // < gcGoalUtilization. One reason might be in mostly idle applications,
31- // where goroutines are unlikely to assist at all, so the actual
32- // utilization will be lower than the goal. But this is moot point
33- // because the idle mark workers already soak up idle CPU resources.
34- // These two values are still kept separate however because they are
35- // distinct conceptually, and in previous iterations of the pacer the
36- // distinction was more important.
37- gcBackgroundUtilization = 0.25
38- 
39 // gcCreditSlack is the amount of scan work credit that can15 // gcCreditSlack is the amount of scan work credit that can
40 // accumulate locally before updating gcController.heapScanWork and,16 // accumulate locally before updating gcController.heapScanWork and,
41 // optionally, gcController.bgScanCredit. Lower values give a more17 // optionally, gcController.bgScanCredit. Lower values give a more
@@ -73,6 +49,14 @@ const (
73 memoryLimitHeapGoalHeadroomPercent = 349 memoryLimitHeapGoalHeadroomPercent = 3
74)50)
75 51 
52+// gcGoalUtilization is the goal CPU utilization for
53+// marking as a fraction of GOMAXPROCS.
54+//
55+// Increasing the goal utilization will shorten GC cycles as the GC
56+// has more resources behind it, lessening costs from the write barrier,
57+// but comes at the cost of increasing mutator latency.
58+var gcGoalUtilization = gcController.gcRatio
59+ 
76// gcController implements the GC pacing controller that determines60// gcController implements the GC pacing controller that determines
77// when to trigger concurrent garbage collection and how much marking61// when to trigger concurrent garbage collection and how much marking
78// work to do in mutator assists and background marking.62// work to do in mutator assists and background marking.
@@ -88,6 +72,11 @@ const (
88var gcController gcControllerState72var gcController gcControllerState
89 73 
90type gcControllerState struct {74type gcControllerState struct {
75+ // gcController.gcRatio be optional, value equals gcratio/100.0.
76+ // Initialized from GOGCRATIO, which in the range of (1, 99).
77+ // Default GOGCRATIO is 25.
78+ gcRatio float64
79+ 
91 // Initialized from GOGC. GOGC=off means no GC.80 // Initialized from GOGC. GOGC=off means no GC.
92 gcPercent atomic.Int3281 gcPercent atomic.Int32
93 82 
@@ -366,11 +355,12 @@ type gcControllerState struct {
366 _ cpu.CacheLinePad355 _ cpu.CacheLinePad
367}356}
368 357 
369-func (c *gcControllerState) init(gcPercent int32, memoryLimit int64) {358+func (c *gcControllerState) init(gcPercent int32, memoryLimit int64, gcRatio float64) {
370 c.heapMinimum = defaultHeapMinimum359 c.heapMinimum = defaultHeapMinimum
371 c.triggered = ^uint64(0)360 c.triggered = ^uint64(0)
372 c.setGCPercent(gcPercent)361 c.setGCPercent(gcPercent)
373 c.setMemoryLimit(memoryLimit)362 c.setMemoryLimit(memoryLimit)
363+ c.setGOGCRatio(gcRatio)
374 c.commit(true) // No sweep phase in the first GC cycle.364 c.commit(true) // No sweep phase in the first GC cycle.
375 // N.B. Don't bother calling traceHeapGoal. Tracing is never enabled at365 // N.B. Don't bother calling traceHeapGoal. Tracing is never enabled at
376 // initialization time.366 // initialization time.
@@ -398,13 +388,13 @@ func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger g
398 // dedicated workers so that the utilization is closest to388 // dedicated workers so that the utilization is closest to
399 // 25%. For small GOMAXPROCS, this would introduce too much389 // 25%. For small GOMAXPROCS, this would introduce too much
400 // error, so we add fractional workers in that case.390 // error, so we add fractional workers in that case.
401- totalUtilizationGoal := float64(procs) * gcBackgroundUtilization391+ totalUtilizationGoal := float64(procs) * gcController.gcRatio
402 dedicatedMarkWorkersNeeded := int64(totalUtilizationGoal + 0.5)392 dedicatedMarkWorkersNeeded := int64(totalUtilizationGoal + 0.5)
403 utilError := float64(dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1393 utilError := float64(dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1
404 const maxUtilError = 0.3394 const maxUtilError = 0.3
405 if utilError < -maxUtilError || utilError > maxUtilError {395 if utilError < -maxUtilError || utilError > maxUtilError {
406 // Rounding put us more than 30% off our goal. With396 // Rounding put us more than 30% off our goal. With
407- // gcBackgroundUtilization of 25%, this happens for397+ // gcController.gcRatio of 25%, this happens for
408 // GOMAXPROCS<=3 or GOMAXPROCS=6. Enable fractional398 // GOMAXPROCS<=3 or GOMAXPROCS=6. Enable fractional
409 // workers to compensate.399 // workers to compensate.
410 if float64(dedicatedMarkWorkersNeeded) > totalUtilizationGoal {400 if float64(dedicatedMarkWorkersNeeded) > totalUtilizationGoal {
@@ -604,7 +594,7 @@ func (c *gcControllerState) endCycle(now int64, procs int, userForced bool) {
604 assistDuration := now - c.markStartTime594 assistDuration := now - c.markStartTime
605 595 
606 // Assume background mark hit its utilization goal.596 // Assume background mark hit its utilization goal.
607- utilization := gcBackgroundUtilization597+ utilization := gcController.gcRatio
608 // Add assist utilization; avoid divide by zero.598 // Add assist utilization; avoid divide by zero.
609 if assistDuration > 0 {599 if assistDuration > 0 {
610 utilization += float64(c.assistTime.Load()) / float64(assistDuration*int64(procs))600 utilization += float64(c.assistTime.Load()) / float64(assistDuration*int64(procs))
@@ -1344,6 +1334,39 @@ func readGOMEMLIMIT() int64 {
1344 return n1334 return n
1345}1335}
1346 1336 
1337+func (c *gcControllerState) setGOGCRatio(in float64) float64 {
1338+ if !c.test {
1339+ assertWorldStoppedOrLockHeld(&mheap_.lock)
1340+ }
1341+ 
1342+ out := c.gcRatio
1343+ c.gcRatio = in
1344+ 
1345+ return out
1346+}
1347+ 
1348+func readGOGCRATIO() float64 {
1349+ p := gogetenv("GOGCRATIO")
1350+ if p == "" {
1351+ return 0.25
1352+ }
1353+ n, ok := parseByteCount(p)
1354+ if !ok {
1355+ print("GOGCRATIO=", p, "\n")
1356+ throw("malformed GOGCRATIO; get the wrong value")
1357+ }
1358+ 
1359+ if n < 1 {
1360+ n = 1
1361+ } else if n > 99 {
1362+ n = 99
1363+ }
1364+ 
1365+ out := float64(n) / 100.0
1366+ 
1367+ return out
1368+}
1369+ 
1347// addIdleMarkWorker attempts to add a new idle mark worker.1370// addIdleMarkWorker attempts to add a new idle mark worker.
1348//1371//
1349// If this returns true, the caller must become an idle mark worker unless1372// If this returns true, the caller must become an idle mark worker unless
@@ -24,6 +24,7 @@ func TestGcPacer(t *testing.T) {
24 name: "Steady",24 name: "Steady",
25 gcPercent: 100,25 gcPercent: 100,
26 memoryLimit: math.MaxInt64,26 memoryLimit: math.MaxInt64,
27+ gcRatio: 0.25,
27 globalsBytes: 32 << 10,28 globalsBytes: 32 << 10,
28 nCores: 8,29 nCores: 8,
29 allocRate: constant(33.0),30 allocRate: constant(33.0),
@@ -49,6 +50,7 @@ func TestGcPacer(t *testing.T) {
49 name: "SteadyBigStacks",50 name: "SteadyBigStacks",
50 gcPercent: 100,51 gcPercent: 100,
51 memoryLimit: math.MaxInt64,52 memoryLimit: math.MaxInt64,
53+ gcRatio: 0.25,
52 globalsBytes: 32 << 10,54 globalsBytes: 32 << 10,
53 nCores: 8,55 nCores: 8,
54 allocRate: constant(132.0),56 allocRate: constant(132.0),
@@ -77,6 +79,7 @@ func TestGcPacer(t *testing.T) {
77 name: "SteadyBigGlobals",79 name: "SteadyBigGlobals",
78 gcPercent: 100,80 gcPercent: 100,
79 memoryLimit: math.MaxInt64,81 memoryLimit: math.MaxInt64,
82+ gcRatio: 0.25,
80 globalsBytes: 128 << 20,83 globalsBytes: 128 << 20,
81 nCores: 8,84 nCores: 8,
82 allocRate: constant(132.0),85 allocRate: constant(132.0),
@@ -105,6 +108,7 @@ func TestGcPacer(t *testing.T) {
105 name: "StepAlloc",108 name: "StepAlloc",
106 gcPercent: 100,109 gcPercent: 100,
107 memoryLimit: math.MaxInt64,110 memoryLimit: math.MaxInt64,
111+ gcRatio: 0.25,
108 globalsBytes: 32 << 10,112 globalsBytes: 32 << 10,
109 nCores: 8,113 nCores: 8,
110 allocRate: constant(33.0).sum(ramp(66.0, 1).delay(50)),114 allocRate: constant(33.0).sum(ramp(66.0, 1).delay(50)),
@@ -128,6 +132,7 @@ func TestGcPacer(t *testing.T) {
128 name: "HeavyStepAlloc",132 name: "HeavyStepAlloc",
129 gcPercent: 100,133 gcPercent: 100,
130 memoryLimit: math.MaxInt64,134 memoryLimit: math.MaxInt64,
135+ gcRatio: 0.25,
131 globalsBytes: 32 << 10,136 globalsBytes: 32 << 10,
132 nCores: 8,137 nCores: 8,
133 allocRate: constant(33).sum(ramp(330, 1).delay(50)),138 allocRate: constant(33).sum(ramp(330, 1).delay(50)),
@@ -151,6 +156,7 @@ func TestGcPacer(t *testing.T) {
151 name: "StepScannableFrac",156 name: "StepScannableFrac",
152 gcPercent: 100,157 gcPercent: 100,
153 memoryLimit: math.MaxInt64,158 memoryLimit: math.MaxInt64,
159+ gcRatio: 0.25,
154 globalsBytes: 32 << 10,160 globalsBytes: 32 << 10,
155 nCores: 8,161 nCores: 8,
156 allocRate: constant(128.0),162 allocRate: constant(128.0),
@@ -176,6 +182,7 @@ func TestGcPacer(t *testing.T) {
176 name: "HighGOGC",182 name: "HighGOGC",
177 gcPercent: 1500,183 gcPercent: 1500,
178 memoryLimit: math.MaxInt64,184 memoryLimit: math.MaxInt64,
185+ gcRatio: 0.25,
179 globalsBytes: 32 << 10,186 globalsBytes: 32 << 10,
180 nCores: 8,187 nCores: 8,
181 allocRate: random(7, 0x53).offset(165),188 allocRate: random(7, 0x53).offset(165),
@@ -217,6 +224,7 @@ func TestGcPacer(t *testing.T) {
217 name: "OscAlloc",224 name: "OscAlloc",
218 gcPercent: 100,225 gcPercent: 100,
219 memoryLimit: math.MaxInt64,226 memoryLimit: math.MaxInt64,
227+ gcRatio: 0.25,
220 globalsBytes: 32 << 10,228 globalsBytes: 32 << 10,
221 nCores: 8,229 nCores: 8,
222 allocRate: oscillate(13, 0, 8).offset(67),230 allocRate: oscillate(13, 0, 8).offset(67),
@@ -241,6 +249,7 @@ func TestGcPacer(t *testing.T) {
241 name: "JitterAlloc",249 name: "JitterAlloc",
242 gcPercent: 100,250 gcPercent: 100,
243 memoryLimit: math.MaxInt64,251 memoryLimit: math.MaxInt64,
252+ gcRatio: 0.25,
244 globalsBytes: 32 << 10,253 globalsBytes: 32 << 10,
245 nCores: 8,254 nCores: 8,
246 allocRate: random(13, 0xf).offset(132),255 allocRate: random(13, 0xf).offset(132),
@@ -266,6 +275,7 @@ func TestGcPacer(t *testing.T) {
266 name: "HeavyJitterAlloc",275 name: "HeavyJitterAlloc",
267 gcPercent: 100,276 gcPercent: 100,
268 memoryLimit: math.MaxInt64,277 memoryLimit: math.MaxInt64,
278+ gcRatio: 0.25,
269 globalsBytes: 32 << 10,279 globalsBytes: 32 << 10,
270 nCores: 8,280 nCores: 8,
271 allocRate: random(33.0, 0x0).offset(330),281 allocRate: random(33.0, 0x0).offset(330),
@@ -295,6 +305,7 @@ func TestGcPacer(t *testing.T) {
295 name: "SmallHeapSlowAlloc",305 name: "SmallHeapSlowAlloc",
296 gcPercent: 100,306 gcPercent: 100,
297 memoryLimit: math.MaxInt64,307 memoryLimit: math.MaxInt64,
308+ gcRatio: 0.25,
298 globalsBytes: 32 << 10,309 globalsBytes: 32 << 10,
299 nCores: 8,310 nCores: 8,
300 allocRate: constant(1.0),311 allocRate: constant(1.0),
@@ -332,6 +343,7 @@ func TestGcPacer(t *testing.T) {
332 name: "MediumHeapSlowAlloc",343 name: "MediumHeapSlowAlloc",
333 gcPercent: 100,344 gcPercent: 100,
334 memoryLimit: math.MaxInt64,345 memoryLimit: math.MaxInt64,
346+ gcRatio: 0.25,
335 globalsBytes: 32 << 10,347 globalsBytes: 32 << 10,
336 nCores: 8,348 nCores: 8,
337 allocRate: constant(1.0),349 allocRate: constant(1.0),
@@ -369,6 +381,7 @@ func TestGcPacer(t *testing.T) {
369 name: "LargeHeapSlowAlloc",381 name: "LargeHeapSlowAlloc",
370 gcPercent: 100,382 gcPercent: 100,
371 memoryLimit: math.MaxInt64,383 memoryLimit: math.MaxInt64,
384+ gcRatio: 0.25,
372 globalsBytes: 32 << 10,385 globalsBytes: 32 << 10,
373 nCores: 8,386 nCores: 8,
374 allocRate: constant(1.0),387 allocRate: constant(1.0),
@@ -407,6 +420,7 @@ func TestGcPacer(t *testing.T) {
407 name: "SteadyMemoryLimit",420 name: "SteadyMemoryLimit",
408 gcPercent: 100,421 gcPercent: 100,
409 memoryLimit: 512 << 20,422 memoryLimit: 512 << 20,
423+ gcRatio: 0.25,
410 globalsBytes: 32 << 10,424 globalsBytes: 32 << 10,
411 nCores: 8,425 nCores: 8,
412 allocRate: constant(33.0),426 allocRate: constant(33.0),
@@ -436,6 +450,7 @@ func TestGcPacer(t *testing.T) {
436 name: "SteadyMemoryLimitNoGCPercent",450 name: "SteadyMemoryLimitNoGCPercent",
437 gcPercent: -1,451 gcPercent: -1,
438 memoryLimit: 512 << 20,452 memoryLimit: 512 << 20,
453+ gcRatio: 0.25,
439 globalsBytes: 32 << 10,454 globalsBytes: 32 << 10,
440 nCores: 8,455 nCores: 8,
441 allocRate: constant(33.0),456 allocRate: constant(33.0),
@@ -465,6 +480,7 @@ func TestGcPacer(t *testing.T) {
465 name: "ExceedMemoryLimit",480 name: "ExceedMemoryLimit",
466 gcPercent: 100,481 gcPercent: 100,
467 memoryLimit: 512 << 20,482 memoryLimit: 512 << 20,
483+ gcRatio: 0.25,
468 globalsBytes: 32 << 10,484 globalsBytes: 32 << 10,
469 nCores: 8,485 nCores: 8,
470 allocRate: constant(33.0),486 allocRate: constant(33.0),
@@ -499,6 +515,7 @@ func TestGcPacer(t *testing.T) {
499 name: "ExceedMemoryLimitNoGCPercent",515 name: "ExceedMemoryLimitNoGCPercent",
500 gcPercent: -1,516 gcPercent: -1,
501 memoryLimit: 512 << 20,517 memoryLimit: 512 << 20,
518+ gcRatio: 0.25,
502 globalsBytes: 32 << 10,519 globalsBytes: 32 << 10,
503 nCores: 8,520 nCores: 8,
504 allocRate: constant(33.0),521 allocRate: constant(33.0),
@@ -538,6 +555,7 @@ func TestGcPacer(t *testing.T) {
538 name: "MaintainMemoryLimit",555 name: "MaintainMemoryLimit",
539 gcPercent: 100,556 gcPercent: 100,
540 memoryLimit: 512 << 20,557 memoryLimit: 512 << 20,
558+ gcRatio: 0.25,
541 globalsBytes: 32 << 10,559 globalsBytes: 32 << 10,
542 nCores: 8,560 nCores: 8,
543 allocRate: constant(33.0),561 allocRate: constant(33.0),
@@ -571,6 +589,7 @@ func TestGcPacer(t *testing.T) {
571 name: "MaintainMemoryLimitNoGCPercent",589 name: "MaintainMemoryLimitNoGCPercent",
572 gcPercent: -1,590 gcPercent: -1,
573 memoryLimit: 512 << 20,591 memoryLimit: 512 << 20,
592+ gcRatio: 0.25,
574 globalsBytes: 32 << 10,593 globalsBytes: 32 << 10,
575 nCores: 8,594 nCores: 8,
576 allocRate: constant(33.0),595 allocRate: constant(33.0),
@@ -607,7 +626,7 @@ func TestGcPacer(t *testing.T) {
607 t.Run(e.name, func(t *testing.T) {626 t.Run(e.name, func(t *testing.T) {
608 t.Parallel()627 t.Parallel()
609 628 
610- c := NewGCController(e.gcPercent, e.memoryLimit)629+ c := NewGCController(e.gcPercent, e.memoryLimit, e.gcRatio)
611 var bytesAllocatedBlackLast int64630 var bytesAllocatedBlackLast int64
612 results := make([]gcCycleResult, 0, e.length)631 results := make([]gcCycleResult, 0, e.length)
613 for i := 0; i < e.length; i++ {632 for i := 0; i < e.length; i++ {
@@ -762,6 +781,7 @@ type gcExecTest struct {
762 781 
763 gcPercent int782 gcPercent int
764 memoryLimit int64783 memoryLimit int64
784+ gcRatio float64
765 globalsBytes uint64785 globalsBytes uint64
766 nCores int786 nCores int
767 787 
@@ -1034,7 +1054,7 @@ func applyMemoryLimitHeapGoalHeadroom(goal uint64) uint64 {
1034 1054 
1035func TestIdleMarkWorkerCount(t *testing.T) {1055func TestIdleMarkWorkerCount(t *testing.T) {
1036 const workers = 101056 const workers = 10
1037- c := NewGCController(100, math.MaxInt64)1057+ c := NewGCController(100, math.MaxInt64, 0.25)
1038 c.SetMaxIdleMarkWorkers(workers)1058 c.SetMaxIdleMarkWorkers(workers)
1039 for i := 0; i < workers; i++ {1059 for i := 0; i < workers; i++ {
1040 if !c.NeedIdleMarkWorker() {1060 if !c.NeedIdleMarkWorker() {