Skip to content

Commit 037ac2b

Browse files
committed
cmd/compile: add -smallframes gc flag for GC latency diagnosis
Shrinks the size of things that can be stack allocated from 10M to 128k for declared variables and from 64k to 16k for implicit allocations (new(T), &T{}, etc). Usage: "go build -gcflags -smallframes hello.go" An earlier GOEXPERIMENT version of this caused only one problem, when a gc-should-detect-oversize-stack test no longer had an oversized stack to detect. The change was converted to a flag to make it easier to access (for diagnosing "long" GC-related single-thread pauses) and to remove interference with the test. Includes test to verify behavior. Updates #27732. Change-Id: I1255d484331e77185e07c78389a8b594041204c2 Reviewed-on: https://go-review.googlesource.com/c/go/+/180817 Run-TryBot: David Chase <[email protected]> Reviewed-by: Keith Randall <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent f31b7b9 commit 037ac2b

File tree

4 files changed

+41
-4
lines changed

4 files changed

+41
-4
lines changed

src/cmd/compile/internal/gc/go.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,21 @@ import (
1414

1515
const (
1616
BADWIDTH = types.BADWIDTH
17+
)
1718

19+
var (
1820
// maximum size variable which we will allocate on the stack.
1921
// This limit is for explicit variable declarations like "var x T" or "x := ...".
20-
maxStackVarSize = 10 * 1024 * 1024
22+
// Note: the flag smallframes can update this value.
23+
maxStackVarSize = int64(10 * 1024 * 1024)
2124

2225
// maximum size of implicit variables that we will allocate on the stack.
2326
// p := new(T) allocating T on the stack
2427
// p := &T{} allocating T on the stack
2528
// s := make([]T, n) allocating [n]T on the stack
2629
// s := []byte("...") allocating [n]byte on the stack
27-
maxImplicitStackVarSize = 64 * 1024
30+
// Note: the flag smallframes can update this value.
31+
maxImplicitStackVarSize = int64(64 * 1024)
2832
)
2933

3034
// isRuntimePkg reports whether p is package runtime.

src/cmd/compile/internal/gc/main.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ func Main(archInit func(*Arch)) {
190190
Nacl = objabi.GOOS == "nacl"
191191
Wasm := objabi.GOARCH == "wasm"
192192

193+
// Whether the limit for stack-allocated objects is much smaller than normal.
194+
// This can be helpful for diagnosing certain causes of GC latency. See #27732.
195+
smallFrames := false
196+
193197
flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
194198
flag.BoolVar(&compiling_std, "std", false, "compiling standard library")
195199
objabi.Flagcount("%", "debug non-static initializers", &Debug['%'])
@@ -261,13 +265,19 @@ func Main(archInit func(*Arch)) {
261265
flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`")
262266
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
263267
flag.BoolVar(&newescape, "newescape", true, "enable new escape analysis")
268+
flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
264269
flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
265270
objabi.Flagparse(usage)
266271

267272
// Record flags that affect the build result. (And don't
268273
// record flags that don't, since that would cause spurious
269274
// changes in the binary.)
270-
recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "newescape", "dwarfbasentries")
275+
recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "newescape", "dwarfbasentries", "smallFrames")
276+
277+
if smallFrames {
278+
maxStackVarSize = 128 * 1024
279+
maxImplicitStackVarSize = 16 * 1024
280+
}
271281

272282
Ctxt.Flag_shared = flag_dynlink || flag_shared
273283
Ctxt.Flag_dynlink = flag_dynlink

src/cmd/compile/internal/gc/walk.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,7 @@ opswitch:
13931393
// Allocate a [n]byte of the right size.
13941394
t := types.NewArray(types.Types[TUINT8], int64(len(sc)))
13951395
var a *Node
1396-
if n.Esc == EscNone && len(sc) <= maxImplicitStackVarSize {
1396+
if n.Esc == EscNone && len(sc) <= int(maxImplicitStackVarSize) {
13971397
a = nod(OADDR, temp(t), nil)
13981398
} else {
13991399
a = callnew(t)

test/fixedbugs/issue27732a.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// errorcheck -0 -m -l -smallframes -newescape=true
2+
3+
// Copyright 2019 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// This checks that the -smallframes flag forces a large variable to heap.
8+
9+
package main
10+
11+
const (
12+
bufferLen = 200000
13+
)
14+
15+
type kbyte []byte
16+
type circularBuffer [bufferLen]kbyte
17+
18+
var sink byte
19+
20+
func main() {
21+
var c circularBuffer // ERROR "moved to heap: c$"
22+
sink = c[0][0]
23+
}

0 commit comments

Comments
 (0)