aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/mgcpacer.go
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2021-04-12 22:33:54 +0000
committerMichael Knyszek <mknyszek@google.com>2021-10-29 18:35:20 +0000
commit9ac1ee2d464eff73077afda83677f155bd69c6b8 (patch)
treecd0191a02db49cd17f5d95f9f9b81a35889718d9 /src/runtime/mgcpacer.go
parent8e112a7c2a814de9156f68dc8b167e4ef8c98c52 (diff)
downloadgo-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.go29
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.