From d1b544c7eb00d54d7298292c1860a965ecc93d41 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Sun, 23 Apr 2017 17:31:15 -0700 Subject: [PATCH] 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 --- src/cmd/compile/internal/gc/init.go | 78 +++++++++++++++++++++++++--- src/cmd/compile/internal/gc/sinit.go | 5 +- 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go index bbdf19d110..93ae2410cd 100644 --- a/src/cmd/compile/internal/gc/init.go +++ b/src/cmd/compile/internal/gc/init.go @@ -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) + } +} diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index 1a1dbc0de7..613cdf6e74 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -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) } -- 2.50.0