diff options
author | Michael Anthony Knyszek <mknyszek@google.com> | 2021-04-12 22:33:54 +0000 |
---|---|---|
committer | Michael Knyszek <mknyszek@google.com> | 2021-10-29 18:35:20 +0000 |
commit | 9ac1ee2d464eff73077afda83677f155bd69c6b8 (patch) | |
tree | cd0191a02db49cd17f5d95f9f9b81a35889718d9 /src/runtime/mgcpacer.go | |
parent | 8e112a7c2a814de9156f68dc8b167e4ef8c98c52 (diff) | |
download | go-9ac1ee2d464eff73077afda83677f155bd69c6b8.tar.gz go-9ac1ee2d464eff73077afda83677f155bd69c6b8.zip |
runtime: track the amount of scannable allocated stack for the GC pacer
This change adds two fields to gcControllerState: stackScan, used for
pacing decisions, and scannableStackSize, which directly tracks the
amount of space allocated for inuse stacks that will be scanned.
scannableStackSize is not updated directly, but is instead flushed from
each P when at an least 8 KiB delta has accumulated. This helps reduce
issues with atomics contention for newly created goroutines. Stack
growth paths are largely unaffected.
StackGrowth-48 51.4ns ± 0% 51.4ns ± 0% ~ (p=0.927 n=10+10)
StackGrowthDeep-48 6.14µs ± 3% 6.25µs ± 4% ~ (p=0.090 n=10+9)
CreateGoroutines-48 273ns ± 1% 273ns ± 1% ~ (p=0.676 n=9+10)
CreateGoroutinesParallel-48 65.5ns ± 5% 66.6ns ± 7% ~ (p=0.340 n=9+9)
CreateGoroutinesCapture-48 2.06µs ± 1% 2.07µs ± 4% ~ (p=0.217 n=10+10)
CreateGoroutinesSingle-48 550ns ± 3% 563ns ± 4% +2.41% (p=0.034 n=8+10)
For #44167.
Change-Id: Id1800d41d3a6c211b43aeb5681c57c0dc8880daf
Reviewed-on: https://go-review.googlesource.com/c/go/+/309589
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/runtime/mgcpacer.go')
-rw-r--r-- | src/runtime/mgcpacer.go | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/src/runtime/mgcpacer.go b/src/runtime/mgcpacer.go index 094dcc701a..9cc7cf99db 100644 --- a/src/runtime/mgcpacer.go +++ b/src/runtime/mgcpacer.go @@ -47,6 +47,10 @@ const ( // defaultHeapMinimum is the value of heapMinimum for GOGC==100. defaultHeapMinimum = 4 << 20 + + // scannableStackSizeSlack is the bytes of stack space allocated or freed + // that can accumulate on a P before updating gcController.stackSize. + scannableStackSizeSlack = 8 << 10 ) func init() { @@ -166,6 +170,18 @@ type gcControllerState struct { // Read and written atomically or with the world stopped. heapScan uint64 + // stackScan is a snapshot of scannableStackSize taken at each GC + // STW pause and is used in pacing decisions. + // + // Updated only while the world is stopped. + stackScan uint64 + + // scannableStackSize is the amount of allocated goroutine stack space in + // use by goroutines. + // + // Read and updated atomically. + scannableStackSize uint64 + // heapMarked is the number of bytes marked by the previous // GC. After mark termination, heapLive == heapMarked, but // unlike heapLive, heapMarked does not change until the @@ -276,6 +292,7 @@ func (c *gcControllerState) startCycle(markStartTime int64) { c.fractionalMarkTime = 0 c.idleMarkTime = 0 c.markStartTime = markStartTime + c.stackScan = atomic.Load64(&c.scannableStackSize) // Ensure that the heap goal is at least a little larger than // the current live heap size. This may not be the case if GC @@ -686,6 +703,18 @@ func (c *gcControllerState) update(dHeapLive, dHeapScan int64) { } } +func (c *gcControllerState) addScannableStack(pp *p, amount int64) { + if pp == nil { + atomic.Xadd64(&c.scannableStackSize, amount) + return + } + pp.scannableStackSizeDelta += amount + if pp.scannableStackSizeDelta >= scannableStackSizeSlack || pp.scannableStackSizeDelta <= -scannableStackSizeSlack { + atomic.Xadd64(&c.scannableStackSize, pp.scannableStackSizeDelta) + pp.scannableStackSizeDelta = 0 + } +} + // commit sets the trigger ratio and updates everything // derived from it: the absolute trigger, the heap goal, mark pacing, // and sweep pacing. |