mirror of
https://github.com/golang/go
synced 2024-11-23 19:00:04 -07:00
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 <khr@golang.org>
This commit is contained in:
parent
b666f2860b
commit
d1b544c7eb
@ -4,7 +4,9 @@
|
||||
|
||||
package gc
|
||||
|
||||
import "cmd/compile/internal/types"
|
||||
import (
|
||||
"cmd/compile/internal/types"
|
||||
)
|
||||
|
||||
// A function named init is a special case.
|
||||
// It is called by the initialization before main is run.
|
||||
@ -114,8 +116,9 @@ func fninit(n []*Node) {
|
||||
// (6)
|
||||
for _, s := range types.InitSyms {
|
||||
if s.Def != nil && s != initsym {
|
||||
// could check that it is fn of no args/returns
|
||||
a = nod(OCALL, asNode(s.Def), nil)
|
||||
n := asNode(s.Def)
|
||||
n.checkInitFuncSignature()
|
||||
a = nod(OCALL, n, nil)
|
||||
r = append(r, a)
|
||||
}
|
||||
}
|
||||
@ -124,11 +127,63 @@ func fninit(n []*Node) {
|
||||
r = append(r, nf...)
|
||||
|
||||
// (8)
|
||||
// could check that it is fn of no args/returns
|
||||
for i := 0; i < renameinitgen; i++ {
|
||||
s := lookupN("init.", i)
|
||||
a = nod(OCALL, asNode(s.Def), nil)
|
||||
r = append(r, a)
|
||||
|
||||
// maxInlineInitCalls is the threshold at which we switch
|
||||
// from generating calls inline to generating a static array
|
||||
// of functions and calling them in a loop.
|
||||
// See CL 41500 for more discussion.
|
||||
const maxInlineInitCalls = 500
|
||||
|
||||
if renameinitgen < maxInlineInitCalls {
|
||||
// Not many init functions. Just call them all directly.
|
||||
for i := 0; i < renameinitgen; i++ {
|
||||
s := lookupN("init.", i)
|
||||
n := asNode(s.Def)
|
||||
n.checkInitFuncSignature()
|
||||
a = nod(OCALL, n, nil)
|
||||
r = append(r, a)
|
||||
}
|
||||
} else {
|
||||
// Lots of init functions.
|
||||
// Set up an array of functions and loop to call them.
|
||||
// This is faster to compile and similar at runtime.
|
||||
|
||||
// Build type [renameinitgen]func().
|
||||
typ := types.NewArray(functype(nil, nil, nil), int64(renameinitgen))
|
||||
|
||||
// Make and fill array.
|
||||
fnarr := staticname(typ)
|
||||
fnarr.Name.SetReadonly(true)
|
||||
for i := 0; i < renameinitgen; i++ {
|
||||
s := lookupN("init.", i)
|
||||
lhs := nod(OINDEX, fnarr, nodintconst(int64(i)))
|
||||
rhs := asNode(s.Def)
|
||||
rhs.checkInitFuncSignature()
|
||||
as := nod(OAS, lhs, rhs)
|
||||
as = typecheck(as, Etop)
|
||||
genAsStatic(as)
|
||||
}
|
||||
|
||||
// Generate a loop that calls each function in turn.
|
||||
// for i := 0; i < renameinitgen; i++ {
|
||||
// fnarr[i]()
|
||||
// }
|
||||
i := temp(types.Types[TINT])
|
||||
fnidx := nod(OINDEX, fnarr, i)
|
||||
fnidx.SetBounded(true)
|
||||
|
||||
zero := nod(OAS, i, nodintconst(0))
|
||||
cond := nod(OLT, i, nodintconst(int64(renameinitgen)))
|
||||
incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
|
||||
body := nod(OCALL, fnidx, nil)
|
||||
|
||||
loop := nod(OFOR, cond, incr)
|
||||
loop.Nbody.Set1(body)
|
||||
loop.Ninit.Set1(zero)
|
||||
|
||||
loop = typecheck(loop, Etop)
|
||||
loop = walkstmt(loop)
|
||||
r = append(r, loop)
|
||||
}
|
||||
|
||||
// (9)
|
||||
@ -151,3 +206,10 @@ func fninit(n []*Node) {
|
||||
Curfn = nil
|
||||
funccompile(fn)
|
||||
}
|
||||
|
||||
func (n *Node) checkInitFuncSignature() {
|
||||
ft := n.Type.FuncType()
|
||||
if ft.Receiver.Fields().Len()+ft.Params.Fields().Len()+ft.Results.Fields().Len() > 0 {
|
||||
Fatalf("init function cannot have receiver, params, or results: %v (%v)", n, n.Type)
|
||||
}
|
||||
}
|
||||
|
@ -1359,7 +1359,10 @@ func genAsStatic(as *Node) {
|
||||
Fatalf("genAsStatic: lhs %v", as.Left)
|
||||
}
|
||||
|
||||
if as.Right.Op != OLITERAL {
|
||||
switch {
|
||||
case as.Right.Op == OLITERAL:
|
||||
case as.Right.Op == ONAME && as.Right.Class() == PFUNC:
|
||||
default:
|
||||
Fatalf("genAsStatic: rhs %v", as.Right)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user