Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: runtime: GC pacer redesign #44167

Open
mknyszek opened this issue Feb 8, 2021 · 7 comments
Open

proposal: runtime: GC pacer redesign #44167

mknyszek opened this issue Feb 8, 2021 · 7 comments
Assignees
Projects
Milestone

Comments

@mknyszek
Copy link
Contributor

@mknyszek mknyszek commented Feb 8, 2021

GC Pacer Redesign

Author: Michael Knyszek (with lots of input from Austin Clements, David Chase, and Jeremy Faller)

Abstract

Go's tracing garbage collector runs concurrently with the application, and thus requires an algorithm to determine when to start a new cycle. In the runtime, this algorithm is referred to as the pacer. Until now, the garbage collector has framed this process as an optimization problem, utilizing a proportional controller to achieve a desired stopping-point (that is, the cycle completes just as the heap reaches a certain size) as well as a desired CPU utilization. While this approach has served Go well for a long time, the design has accrued many corner cases due to resolved issues, as well as a backlog of unresolved issues.

I propose redesigning the garbage collector's pacer from the ground up to capture the things it does well and eliminate the problems that have been discovered.

More specifically, I propose:

  1. Including all non-heap sources of GC work (stacks, globals) in pacing decisions.
  2. Reframing the pacing problem as a search problem, "solved" by a proportional-integral controller,
  3. Extending the hard heap goal to the worst-case heap goal of the next GC,

(1) will resolve long-standing issues with small heap sizes, allowing the Go garbage collector to scale down and act more predictably in general.
(2) will eliminate offset error present in the current design, will allow turning off mark-assist almost entirely outside of exceptional cases, improving allocation latency, and will enable clearer designs for setting memory limits on Go applications.
(3) will enable smooth and consistent response to large changes in the live heap size with large GOGC values.

Full design

Found here.

@mknyszek mknyszek added this to the Go1.17 milestone Feb 8, 2021
@mknyszek mknyszek self-assigned this Feb 8, 2021
@gopherbot gopherbot added the Proposal label Feb 8, 2021
@gopherbot
Copy link

@gopherbot gopherbot commented Feb 8, 2021

Change https://golang.org/cl/290489 mentions this issue: design: add GC pacer redesign

@ianlancetaylor ianlancetaylor added this to Incoming in Proposals Feb 8, 2021
@mknyszek
Copy link
Contributor Author

@mknyszek mknyszek commented Feb 8, 2021

By the way: this design feels solid to me, but has not gone through any rounds of feedback yet. In the interest of transparency, I'm hoping to get feedback and work on this here on GitHub going forward.

So, given that, I would not be surprised if there are errors in the document. Please take a look when you have a chance!

CC @randall77 @jeremyfaller @dr2chase @aclements

@storozhukBM
Copy link

@storozhukBM storozhukBM commented Feb 12, 2021

@mknyszek

Do I understand correctly that the forcegcperiod is required because the current pacer does not consider non-heap sources of GC work? Is it necessary to call GC periodically in application with effectively zero heap allocation rate to collects stacks, etc.? If I understood your proposal correctly, it seems like it should be possible to remove these periodic calls of GC, and applications that don't create new goroutines and don't allocate anything on heap should never trigger garbage collections, which is a good benefit by itself.

@mknyszek
Copy link
Contributor Author

@mknyszek mknyszek commented Feb 13, 2021

@storozhukBM forcegcperiod is pretty much completely separate from this proposal. Not accounting for non-heap sources of work more directly affects the minimum heap size and whether the GC reaches its goals. Related though is #44163, which is partly motivated by some of the ill effects of the forcegcperiod trigger.

I believe forcegcperiod exists these days to help finalizers run in long-running mostly-idle programs. For example, if you have an external resource tied to the finalizer, you want that cleaned up eventually (and sooner, probably). Going by the API it need not ever run. But, then we could just turn the forced GC on only if there's active finalizers or something, so I'm not convinced that's the only reason. Shrinking stacks periodically also might be a reason, but that seems less critical.

Anyway, I have to look into this again so don't quote me. My memory is hazy. :) I'll dig into the reasons why next week (I don't see them documented anywhere).

gopherbot pushed a commit to golang/proposal that referenced this issue Feb 16, 2021
For golang/go#44167.

Change-Id: I468aa78edb8588b4e48008ad44cecc08544a8f48
Reviewed-on: https://go-review.googlesource.com/c/proposal/+/290489
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
@gopherbot
Copy link

@gopherbot gopherbot commented Feb 16, 2021

Change https://golang.org/cl/292789 mentions this issue: design: add user-configurable memory target

@gopherbot
Copy link

@gopherbot gopherbot commented Feb 18, 2021

Change https://golang.org/cl/293790 mentions this issue: design: regenerate graphs for GC pacer redesign

gopherbot pushed a commit to golang/proposal that referenced this issue Feb 19, 2021
A couple of the graphs were wrong (from the wrong scenario, that is)
because I copied them in manually. Fatal mistake.

Regenerate the graphs following the usual pipeline. Because there's
a degree of jitter and randomness in these graphs they end up slightly
different, but they're all mostly the same.

By regenerating these graphs, it also adds a new line to each graph
for the live heap size. I think this is nice for readability, so I'll
let that get updated too.

For golang/go#44167.

Change-Id: I097f812ba07ca7fd740d8460e2830de6492b3945
Reviewed-on: https://go-review.googlesource.com/c/proposal/+/293790
Reviewed-by: Michael Pratt <mpratt@google.com>
@gopherbot
Copy link

@gopherbot gopherbot commented Feb 23, 2021

Change https://golang.org/cl/295509 mentions this issue: design: add initial conditions section to GC pacer redesign

gopherbot pushed a commit to golang/proposal that referenced this issue Feb 23, 2021
I realized I neglected to talk about initial conditions, even though all
the simulations clearly set *something*.

For golang/go#44167.

Change-Id: Ia1727d5c068847e9192bf87bc1b6a5f0bb832303
Reviewed-on: https://go-review.googlesource.com/c/proposal/+/295509
Reviewed-by: Michael Pratt <mpratt@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Proposals
Incoming
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants