Skip to content

Commit d1b544c

Browse files
committed
cmd/compile: avoid giant init functions due to many user inits
We generate code that calls each user init function one at a time. When there are lots of user init functions, usually due to generated code, like test/rotate* or github.com/juju/govmomi/vim25/types, we can end up with a giant function, which can be slow to compile. This CL puts in an escape valve. When there are more than 500 functions, instead of doing: init.0() init.1() // ... we construct a static array of functions: var fns = [...]func(){init.0, init.1, ... } and call them in a loop. This generates marginally bigger, marginally worse code, so we restrict it to cases in which it might start to matter. 500 was selected as a mostly arbitrary threshold for "lots". Each call uses two Progs, one for PCDATA and one for the call, so at 500 calls we use ~1000 Progs. At concurrency==8, we get a Prog cache of about 1000 Progs per worker. So a threshold of 500 should more or less avoid exhausting the Prog cache in most cases. Change-Id: I276b887173ddbf65b2164ec9f9b5eb04d8c753c2 Reviewed-on: https://go-review.googlesource.com/41500 Reviewed-by: Keith Randall <[email protected]>
1 parent b666f28 commit d1b544c

File tree

2 files changed

+74
-9
lines changed

2 files changed

+74
-9
lines changed

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

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
package gc
66

7-
import "cmd/compile/internal/types"
7+
import (
8+
"cmd/compile/internal/types"
9+
)
810

911
// A function named init is a special case.
1012
// It is called by the initialization before main is run.
@@ -114,8 +116,9 @@ func fninit(n []*Node) {
114116
// (6)
115117
for _, s := range types.InitSyms {
116118
if s.Def != nil && s != initsym {
117-
// could check that it is fn of no args/returns
118-
a = nod(OCALL, asNode(s.Def), nil)
119+
n := asNode(s.Def)
120+
n.checkInitFuncSignature()
121+
a = nod(OCALL, n, nil)
119122
r = append(r, a)
120123
}
121124
}
@@ -124,11 +127,63 @@ func fninit(n []*Node) {
124127
r = append(r, nf...)
125128

126129
// (8)
127-
// could check that it is fn of no args/returns
128-
for i := 0; i < renameinitgen; i++ {
129-
s := lookupN("init.", i)
130-
a = nod(OCALL, asNode(s.Def), nil)
131-
r = append(r, a)
130+
131+
// maxInlineInitCalls is the threshold at which we switch
132+
// from generating calls inline to generating a static array
133+
// of functions and calling them in a loop.
134+
// See CL 41500 for more discussion.
135+
const maxInlineInitCalls = 500
136+
137+
if renameinitgen < maxInlineInitCalls {
138+
// Not many init functions. Just call them all directly.
139+
for i := 0; i < renameinitgen; i++ {
140+
s := lookupN("init.", i)
141+
n := asNode(s.Def)
142+
n.checkInitFuncSignature()
143+
a = nod(OCALL, n, nil)
144+
r = append(r, a)
145+
}
146+
} else {
147+
// Lots of init functions.
148+
// Set up an array of functions and loop to call them.
149+
// This is faster to compile and similar at runtime.
150+
151+
// Build type [renameinitgen]func().
152+
typ := types.NewArray(functype(nil, nil, nil), int64(renameinitgen))
153+
154+
// Make and fill array.
155+
fnarr := staticname(typ)
156+
fnarr.Name.SetReadonly(true)
157+
for i := 0; i < renameinitgen; i++ {
158+
s := lookupN("init.", i)
159+
lhs := nod(OINDEX, fnarr, nodintconst(int64(i)))
160+
rhs := asNode(s.Def)
161+
rhs.checkInitFuncSignature()
162+
as := nod(OAS, lhs, rhs)
163+
as = typecheck(as, Etop)
164+
genAsStatic(as)
165+
}
166+
167+
// Generate a loop that calls each function in turn.
168+
// for i := 0; i < renameinitgen; i++ {
169+
// fnarr[i]()
170+
// }
171+
i := temp(types.Types[TINT])
172+
fnidx := nod(OINDEX, fnarr, i)
173+
fnidx.SetBounded(true)
174+
175+
zero := nod(OAS, i, nodintconst(0))
176+
cond := nod(OLT, i, nodintconst(int64(renameinitgen)))
177+
incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
178+
body := nod(OCALL, fnidx, nil)
179+
180+
loop := nod(OFOR, cond, incr)
181+
loop.Nbody.Set1(body)
182+
loop.Ninit.Set1(zero)
183+
184+
loop = typecheck(loop, Etop)
185+
loop = walkstmt(loop)
186+
r = append(r, loop)
132187
}
133188

134189
// (9)
@@ -151,3 +206,10 @@ func fninit(n []*Node) {
151206
Curfn = nil
152207
funccompile(fn)
153208
}
209+
210+
func (n *Node) checkInitFuncSignature() {
211+
ft := n.Type.FuncType()
212+
if ft.Receiver.Fields().Len()+ft.Params.Fields().Len()+ft.Results.Fields().Len() > 0 {
213+
Fatalf("init function cannot have receiver, params, or results: %v (%v)", n, n.Type)
214+
}
215+
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1359,7 +1359,10 @@ func genAsStatic(as *Node) {
13591359
Fatalf("genAsStatic: lhs %v", as.Left)
13601360
}
13611361

1362-
if as.Right.Op != OLITERAL {
1362+
switch {
1363+
case as.Right.Op == OLITERAL:
1364+
case as.Right.Op == ONAME && as.Right.Class() == PFUNC:
1365+
default:
13631366
Fatalf("genAsStatic: rhs %v", as.Right)
13641367
}
13651368

0 commit comments

Comments
 (0)