]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: avoid giant init functions due to many user inits
authorJosh Bleecher Snyder <josharian@gmail.com>
Mon, 24 Apr 2017 00:31:15 +0000 (17:31 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Mon, 1 May 2017 18:00:11 +0000 (18:00 +0000)
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>
src/cmd/compile/internal/gc/init.go
src/cmd/compile/internal/gc/sinit.go

index bbdf19d1109d79c20438f63f102c7f24884b9b0a..93ae2410cd97d38d46ea563182587f5b8f0544f6 100644 (file)
@@ -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)
+       }
+}
index 1a1dbc0de72973be5d9475f87d023ba904e3bc72..613cdf6e74f6590d096ffc9c66f4bc1dfed6e7b0 100644 (file)
@@ -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)
        }