diff options
| author | Keith Randall <khr@golang.org> | 2021-08-28 15:50:52 -0700 |
|---|---|---|
| committer | Keith Randall <khr@google.com> | 2022-05-12 22:32:42 +0000 |
| commit | 016d7552138077741a9c3fdadc73c0179f5d3ff7 (patch) | |
| tree | cfe978bcbb00bb7e67a610af16fe5706cd181723 /src/runtime/stack.go | |
| parent | da0a6f4b57fc9eb4779c295bc9620c5ed271a641 (diff) | |
| download | go-016d7552138077741a9c3fdadc73c0179f5d3ff7.tar.xz | |
runtime: measure stack usage; start stacks larger if needed
Measure the average stack size used by goroutines at every GC. When
starting a new goroutine, allocate an initial goroutine stack of that
average size. Intuition is that we'll waste at most 2x in stack space
because only half the goroutines can be below average. In turn, we
avoid some of the early stack growth / copying needed in the average
case.
More details in the design doc at: https://docs.google.com/document/d/1YDlGIdVTPnmUiTAavlZxBI1d9pwGQgZT7IKFKlIXohQ/edit?usp=sharing
name old time/op new time/op delta
Issue18138 95.3µs ± 0% 67.3µs ±13% -29.35% (p=0.000 n=9+10)
Fixes #18138
Change-Id: Iba34d22ed04279da7e718bbd569bbf2734922eaa
Reviewed-on: https://go-review.googlesource.com/c/go/+/345889
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Diffstat (limited to 'src/runtime/stack.go')
| -rw-r--r-- | src/runtime/stack.go | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/src/runtime/stack.go b/src/runtime/stack.go index 3a22dcd552..2a7f0bd1c3 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -1436,3 +1436,49 @@ func (r *stackObjectRecord) gcdata() *byte { func morestackc() { throw("attempt to execute system stack code on user stack") } + +// startingStackSize is the amount of stack that new goroutines start with. +// It is a power of 2, and between _FixedStack and maxstacksize, inclusive. +// startingStackSize is updated every GC by tracking the average size of +// stacks scanned during the GC. +var startingStackSize uint32 = _FixedStack + +func gcComputeStartingStackSize() { + if debug.adaptivestackstart == 0 { + return + } + // For details, see the design doc at + // https://docs.google.com/document/d/1YDlGIdVTPnmUiTAavlZxBI1d9pwGQgZT7IKFKlIXohQ/edit?usp=sharing + // The basic algorithm is to track the average size of stacks + // and start goroutines with stack equal to that average size. + // Starting at the average size uses at most 2x the space that + // an ideal algorithm would have used. + // This is just a heuristic to avoid excessive stack growth work + // early in a goroutine's lifetime. See issue 18138. Stacks that + // are allocated too small can still grow, and stacks allocated + // too large can still shrink. + var scannedStackSize uint64 + var scannedStacks uint64 + for _, p := range allp { + scannedStackSize += p.scannedStackSize + scannedStacks += p.scannedStacks + // Reset for next time + p.scannedStackSize = 0 + p.scannedStacks = 0 + } + if scannedStacks == 0 { + startingStackSize = _FixedStack + return + } + avg := scannedStackSize/scannedStacks + _StackGuard + // Note: we add _StackGuard to ensure that a goroutine that + // uses the average space will not trigger a growth. + if avg > uint64(maxstacksize) { + avg = uint64(maxstacksize) + } + if avg < _FixedStack { + avg = _FixedStack + } + // Note: maxstacksize fits in 30 bits, so avg also does. + startingStackSize = uint32(round2(int32(avg))) +} |
