Skip to content

runtime: GC pacing exhibits strange behavior with a low GOGC #37927

Closed
@mknyszek

Description

@mknyszek

As of Go 1.14, GC pacing changed a bit to alleviate issues with an increased allocation rate, related to #35112.

Unfortunately, the pacing change that was made was made in error. Currently, the code for capping the trigger ratio in gcSetTriggerRatio looks like:

    const minTriggerRatio = 0.6                                                                                                                                                                             
                                                                                                                                                                                                            
    // Set the trigger ratio, capped to reasonable bounds.                                                                                                                                                  
    if triggerRatio < minTriggerRatio {                                                                                                                                                                     
        // This can happen if the mutator is allocating very                                                                                                                                                
        // quickly or the GC is scanning very slowly.                                                                                                                                                       
        triggerRatio = minTriggerRatio                                                                                                                                                                      
    } else if gcpercent >= 0 {                                                                                                                                                                              
        // Ensure there's always a little margin so that the                                                                                                                                                
        // mutator assist ratio isn't infinity.                                                                                                                                                             
        maxTriggerRatio := 0.95 * float64(gcpercent) / 100                                                                                                                                                  
        if triggerRatio > maxTriggerRatio {                                                                                                                                                                 
            triggerRatio = maxTriggerRatio                                                                                                                                                                  
        }                                                                                                                                                                                                   
    }                                                                                                                                                                                                       
    memstats.triggerRatio = triggerRatio

Consider the case where gcpercent == 3 as in the Go 1.14 log found here. If the input trigger ratio is < 0.6, then we'll set it at 0.6... but if it's greater, then we'll always set it to 0.95*3/100, or 0.0285. Given that gcpercent is 3, the latter is the correct behavior. In fact, when we see a 0.6 in a gcpacertrace, we also see a spike in heap use compared to Go 1.13. This is not good, since we're not respecting the trade-off.

I think the fix is to scale the minTriggerRatio just like the maxTriggerRatio, though it probably stops making sense to have a minimum at some point (it gets so close to 0 anyway).

We should fix this and probably backport it to Go 1.14. This breaks the promise of GOGC in some cases, which I think counts as a correctness bug?

CC @aclements @randall77

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions