aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/mgcmark.go
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2015-10-04 20:16:57 -0700
committerAustin Clements <austin@google.com>2015-10-09 19:38:52 +0000
commit89c341c5e969b571a9475b263b237a8a25efd76b (patch)
tree7887360ff624d90d6bf15c3a0a018049f030f537 /src/runtime/mgcmark.go
parent9e77c898688529c4d73ad8912d47b20e679a2cfa (diff)
downloadgo-89c341c5e969b571a9475b263b237a8a25efd76b.tar.xz
runtime: directly track GC assist balance
Currently we track the per-G GC assist balance as two monotonically increasing values: the bytes allocated by the G this cycle (gcalloc) and the scan work performed by the G this cycle (gcscanwork). The assist balance is hence assistRatio*gcalloc - gcscanwork. This works, but has two important downsides: 1) It requires floating-point math to figure out if a G is in debt or not. This makes it inappropriate to check for assist debt in the hot path of mallocgc, so we only do this when a G allocates a new span. As a result, Gs can operate "in the red", leading to under-assist and extended GC cycle length. 2) Revising the assist ratio during a GC cycle can lead to an "assist burst". If you think of plotting the scan work performed versus heaps size, the assist ratio controls the slope of this line. However, in the current system, the target line always passes through 0 at the heap size that triggered GC, so if the runtime increases the assist ratio, there has to be a potentially large assist to jump from the current amount of scan work up to the new target scan work for the current heap size. This commit replaces this approach with directly tracking the GC assist balance in terms of allocation credit bytes. Allocating N bytes simply decreases this by N and assisting raises it by the amount of scan work performed divided by the assist ratio (to get back to bytes). This will make it cheap to figure out if a G is in debt, which will let us efficiently check if an assist is necessary *before* performing an allocation and hence keep Gs "in the black". This also fixes assist bursts because the assist ratio is now in terms of *remaining* work, rather than work from the beginning of the GC cycle. Hence, the plot of scan work versus heap size becomes continuous: we can revise the slope, but this slope always starts from where we are right now, rather than where we were at the beginning of the cycle. Change-Id: Ia821c5f07f8a433e8da7f195b52adfedd58bdf2c Reviewed-on: https://go-review.googlesource.com/15408 Reviewed-by: Rick Hudson <rlh@golang.org>
Diffstat (limited to 'src/runtime/mgcmark.go')
-rw-r--r--src/runtime/mgcmark.go32
1 files changed, 19 insertions, 13 deletions
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 2d27d6897d..413274fef1 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -214,9 +214,9 @@ func gcAssistAlloc(size uintptr, allowAssist bool) {
}
// Record allocation.
- gp.gcalloc += size
+ gp.gcAssistBytes -= int64(size)
- if !allowAssist {
+ if !allowAssist || gp.gcAssistBytes >= 0 {
return
}
@@ -229,13 +229,10 @@ func gcAssistAlloc(size uintptr, allowAssist bool) {
return
}
- // Compute the amount of assist scan work we need to do.
- scanWork := int64(gcController.assistRatio*float64(gp.gcalloc)) - gp.gcscanwork
- // scanWork can be negative if the last assist scanned a large
- // object and we're still ahead of our assist goal.
- if scanWork <= 0 {
- return
- }
+ // Compute the amount of scan work we need to do to make the
+ // balance positive.
+ debtBytes := -gp.gcAssistBytes
+ scanWork := int64(gcController.assistWorkPerByte * float64(debtBytes))
retry:
// Steal as much credit as we can from the background GC's
@@ -249,15 +246,18 @@ retry:
if bgScanCredit > 0 {
if bgScanCredit < scanWork {
stolen = bgScanCredit
+ gp.gcAssistBytes += 1 + int64(gcController.assistBytesPerWork*float64(stolen))
} else {
stolen = scanWork
+ gp.gcAssistBytes += debtBytes
}
xaddint64(&gcController.bgScanCredit, -stolen)
scanWork -= stolen
- gp.gcscanwork += stolen
if scanWork == 0 {
+ // We were able to steal all of the credit we
+ // needed.
return
}
}
@@ -291,14 +291,20 @@ retry:
// will be more cache friendly.
gcw := &getg().m.p.ptr().gcw
workDone := gcDrainN(gcw, scanWork)
- // Record that we did this much scan work.
- gp.gcscanwork += workDone
- scanWork -= workDone
// If we are near the end of the mark phase
// dispose of the gcw.
if gcBlackenPromptly {
gcw.dispose()
}
+
+ // Record that we did this much scan work.
+ scanWork -= workDone
+ // Back out the number of bytes of assist credit that
+ // this scan work counts for. The "1+" is a poor man's
+ // round-up, to ensure this adds credit even if
+ // assistBytesPerWork is very low.
+ gp.gcAssistBytes += 1 + int64(gcController.assistBytesPerWork*float64(workDone))
+
// If this is the last worker and we ran out of work,
// signal a completion point.
incnwait := xadd(&work.nwait, +1)