Description
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?