mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
da0a6f4b57
commit
016d755213
12 changed files with 168 additions and 42 deletions
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue