diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/runtime/mgc.go | 74 | ||||
| -rw-r--r-- | src/runtime/mstats.go | 12 |
2 files changed, 49 insertions, 37 deletions
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 70d5795441..aadfccd974 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -179,13 +179,16 @@ func gcinit() { } _ = setGCPercent(readgogc()) + + // Set a reasonable initial GC trigger. + memstats.triggerRatio = 7 / 8.0 memstats.gc_trigger = heapminimum // Compute the goal heap size based on the trigger: // trigger = marked * (1 + triggerRatio) // marked = trigger / (1 + triggerRatio) // goal = marked * (1 + GOGC/100) // = trigger / (1 + triggerRatio) * (1 + GOGC/100) - memstats.next_gc = uint64(float64(memstats.gc_trigger) / (1 + gcController.triggerRatio) * (1 + float64(gcpercent)/100)) + memstats.next_gc = uint64(float64(memstats.gc_trigger) / (1 + memstats.triggerRatio) * (1 + float64(gcpercent)/100)) if gcpercent < 0 { memstats.next_gc = ^uint64(0) } @@ -223,8 +226,8 @@ func setGCPercent(in int32) (out int32) { } gcpercent = in heapminimum = defaultHeapMinimum * uint64(gcpercent) / 100 - if gcController.triggerRatio > float64(gcpercent)/100 { - gcController.triggerRatio = float64(gcpercent) / 100 + if memstats.triggerRatio > float64(gcpercent)/100 { + memstats.triggerRatio = float64(gcpercent) / 100 } // This is either in gcinit or followed by a STW GC, both of // which will reset other stats like memstats.gc_trigger and @@ -330,10 +333,10 @@ var gcMarkWorkerModeStrings = [...]string{ // utilization between assist and background marking to be 25% of // GOMAXPROCS. The high-level design of this algorithm is documented // at https://golang.org/s/go15gcpacing. -var gcController = gcControllerState{ - // Initial trigger ratio guess. - triggerRatio: 7 / 8.0, -} +// +// All fields of gcController are used only during a single mark +// cycle. +var gcController gcControllerState type gcControllerState struct { // scanWork is the total scan work performed this cycle. This @@ -404,14 +407,6 @@ type gcControllerState struct { // beginning of each cycle. fractionalUtilizationGoal float64 - // triggerRatio is the heap growth ratio at which the garbage - // collection cycle should start. E.g., if this is 0.6, then - // GC should start when the live heap has reached 1.6 times - // the heap size marked by the previous cycle. This should be - // ≤ GOGC/100 so the trigger heap size is less than the goal - // heap size. This is updated at the end of of each cycle. - triggerRatio float64 - _ [sys.CacheLineSize]byte // fractionalMarkWorkersNeeded is the number of fractional @@ -440,7 +435,7 @@ func (c *gcControllerState) startCycle() { // first cycle) or may be much smaller (resulting in a large // error response). if memstats.gc_trigger <= heapminimum { - memstats.heap_marked = uint64(float64(memstats.gc_trigger) / (1 + c.triggerRatio)) + memstats.heap_marked = uint64(float64(memstats.gc_trigger) / (1 + memstats.triggerRatio)) } // Re-compute the heap goal for this cycle in case something @@ -551,18 +546,16 @@ func (c *gcControllerState) revise() { c.assistBytesPerWork = float64(heapDistance) / float64(scanWorkExpected) } -// endCycle updates the GC controller state at the end of the -// concurrent part of the GC cycle. -func (c *gcControllerState) endCycle() { +// endCycle computes the trigger ratio for the next cycle. +func (c *gcControllerState) endCycle() float64 { if work.userForced { // Forced GC means this cycle didn't start at the // trigger, so where it finished isn't good // information about how to adjust the trigger. - return + // Just leave it where it is. + return memstats.triggerRatio } - h_t := c.triggerRatio // For debugging - // Proportional response gain for the trigger controller. Must // be in [0, 1]. Lower values smooth out transient effects but // take longer to respond to phase changes. Higher values @@ -590,25 +583,26 @@ func (c *gcControllerState) endCycle() { utilization += float64(c.assistTime) / float64(assistDuration*int64(gomaxprocs)) } - triggerError := goalGrowthRatio - c.triggerRatio - utilization/gcGoalUtilization*(actualGrowthRatio-c.triggerRatio) + triggerError := goalGrowthRatio - memstats.triggerRatio - utilization/gcGoalUtilization*(actualGrowthRatio-memstats.triggerRatio) // Finally, we adjust the trigger for next time by this error, // damped by the proportional gain. - c.triggerRatio += triggerGain * triggerError - if c.triggerRatio < 0 { + triggerRatio := memstats.triggerRatio + triggerGain*triggerError + if triggerRatio < 0 { // This can happen if the mutator is allocating very // quickly or the GC is scanning very slowly. - c.triggerRatio = 0 - } else if c.triggerRatio > goalGrowthRatio*0.95 { + triggerRatio = 0 + } else if triggerRatio > goalGrowthRatio*0.95 { // Ensure there's always a little margin so that the // mutator assist ratio isn't infinity. - c.triggerRatio = goalGrowthRatio * 0.95 + triggerRatio = goalGrowthRatio * 0.95 } if debug.gcpacertrace > 0 { // Print controller state in terms of the design // document. H_m_prev := memstats.heap_marked + h_t := memstats.triggerRatio H_T := memstats.gc_trigger h_a := actualGrowthRatio H_a := memstats.heap_live @@ -628,6 +622,8 @@ func (c *gcControllerState) endCycle() { " u_a/u_g=", u_a/u_g, "\n") } + + return triggerRatio } // enlistWorker encourages another dedicated mark worker to start on @@ -1228,7 +1224,7 @@ func gcStart(mode gcMode, trigger gcTrigger) { work.heapGoal = work.heap0 // Perform mark termination. This will restart the world. - gcMarkTermination() + gcMarkTermination(memstats.triggerRatio) } semrelease(&work.startSema) @@ -1346,14 +1342,14 @@ top: // endCycle depends on all gcWork cache stats being // flushed. This is ensured by mark 2. - gcController.endCycle() + nextTriggerRatio := gcController.endCycle() // Perform mark termination. This will restart the world. - gcMarkTermination() + gcMarkTermination(nextTriggerRatio) } } -func gcMarkTermination() { +func gcMarkTermination(nextTriggerRatio float64) { // World is stopped. // Start marktermination which includes enabling the write barrier. atomic.Store(&gcBlackenEnabled, 0) @@ -1377,7 +1373,7 @@ func gcMarkTermination() { // we don't need to scan gc's internal state). We also // need to switch to g0 so we can shrink the stack. systemstack(func() { - gcMark(startTime) + gcMark(startTime, nextTriggerRatio) // Must return immediately. // The outer function's stack may have moved // during gcMark (it shrinks stacks, including the @@ -1395,7 +1391,7 @@ func gcMarkTermination() { // the concurrent mark process. gcResetMarkState() initCheckmarks() - gcMark(startTime) + gcMark(startTime, memstats.triggerRatio) clearCheckmarks() } @@ -1415,7 +1411,7 @@ func gcMarkTermination() { // At this point all objects will be found during the gcMark which // does a complete STW mark and object scan. setGCPhase(_GCmarktermination) - gcMark(startTime) + gcMark(startTime, memstats.triggerRatio) setGCPhase(_GCoff) // marking is done, turn off wb. gcSweep(work.mode) } @@ -1761,8 +1757,9 @@ func gcMarkWorkAvailable(p *p) bool { // gcMark runs the mark (or, for concurrent GC, mark termination) // All gcWork caches must be empty. // STW is in effect at this point. +// It sets the trigger for the next cycle using nextTriggerRatio. //TODO go:nowritebarrier -func gcMark(start_time int64) { +func gcMark(start_time int64, nextTriggerRatio float64) { if debug.allocfreetrace > 0 { tracegc() } @@ -1858,11 +1855,14 @@ func gcMark(start_time int64) { // Update the marked heap stat. memstats.heap_marked = work.bytesMarked + // Update the GC trigger ratio. + memstats.triggerRatio = nextTriggerRatio + // Trigger the next GC cycle when the allocated heap has grown // by triggerRatio over the marked heap size. Assume that // we're in steady state, so the marked heap size is the // same now as it was at the beginning of the GC cycle. - memstats.gc_trigger = uint64(float64(memstats.heap_marked) * (1 + gcController.triggerRatio)) + memstats.gc_trigger = uint64(float64(memstats.heap_marked) * (1 + memstats.triggerRatio)) if memstats.gc_trigger < heapminimum { memstats.gc_trigger = heapminimum } diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index ae8c1e39c1..95824a9c09 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -94,11 +94,23 @@ type mstats struct { last_gc_nanotime uint64 // last gc (monotonic time) tinyallocs uint64 // number of tiny allocations that didn't cause actual allocation; not exported to go directly + // triggerRatio is the heap growth ratio that triggers marking. + // + // E.g., if this is 0.6, then GC should start when the live + // heap has reached 1.6 times the heap size marked by the + // previous cycle. This should be ≤ GOGC/100 so the trigger + // heap size is less than the goal heap size. This is set + // during mark termination for the next cycle's trigger. + triggerRatio float64 + // gc_trigger is the heap size that triggers marking. // // When heap_live ≥ gc_trigger, the mark phase will start. // This is also the heap size by which proportional sweeping // must be complete. + // + // This is computed from triggerRatio during mark termination + // for the next cycle's trigger. gc_trigger uint64 // heap_live is the number of bytes considered live by the GC. |
