]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/types2: sort to reduce computational complexity of initOrder
authorRobert Findley <rfindley@google.com>
Mon, 6 Dec 2021 03:09:32 +0000 (22:09 -0500)
committerRobert Findley <rfindley@google.com>
Wed, 8 Dec 2021 17:34:34 +0000 (17:34 +0000)
This is a clean port of CL 369434 to types2.

Change-Id: I3f9f80757bfbefb7b0417eef9e7b7c74c4c100b9
Reviewed-on: https://go-review.googlesource.com/c/go/+/369474
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/types2/initorder.go
src/cmd/compile/internal/types2/self_test.go

index 40816276665117ba4bae14c040d515544df9654b..cf6110baa92c0f918b201af3bfe05f9e7f4d2586 100644 (file)
@@ -7,6 +7,7 @@ package types2
 import (
        "container/heap"
        "fmt"
+       "sort"
 )
 
 // initOrder computes the Info.InitOrder for package variables.
@@ -190,6 +191,12 @@ type graphNode struct {
        ndeps      int        // number of outstanding dependencies before this object can be initialized
 }
 
+// cost returns the cost of removing this node, which involves copying each
+// predecessor to each successor (and vice-versa).
+func (n *graphNode) cost() int {
+       return len(n.pred) * len(n.succ)
+}
+
 type nodeSet map[*graphNode]bool
 
 func (s *nodeSet) add(p *graphNode) {
@@ -227,35 +234,48 @@ func dependencyGraph(objMap map[Object]*declInfo) []*graphNode {
                }
        }
 
+       var G, funcG []*graphNode // separate non-functions and functions
+       for _, n := range M {
+               if _, ok := n.obj.(*Func); ok {
+                       funcG = append(funcG, n)
+               } else {
+                       G = append(G, n)
+               }
+       }
+
        // remove function nodes and collect remaining graph nodes in G
        // (Mutually recursive functions may introduce cycles among themselves
        // which are permitted. Yet such cycles may incorrectly inflate the dependency
        // count for variables which in turn may not get scheduled for initialization
        // in correct order.)
-       var G []*graphNode
-       for obj, n := range M {
-               if _, ok := obj.(*Func); ok {
-                       // connect each predecessor p of n with each successor s
-                       // and drop the function node (don't collect it in G)
-                       for p := range n.pred {
-                               // ignore self-cycles
-                               if p != n {
-                                       // Each successor s of n becomes a successor of p, and
-                                       // each predecessor p of n becomes a predecessor of s.
-                                       for s := range n.succ {
-                                               // ignore self-cycles
-                                               if s != n {
-                                                       p.succ.add(s)
-                                                       s.pred.add(p)
-                                                       delete(s.pred, n) // remove edge to n
-                                               }
+       //
+       // Note that because we recursively copy predecessors and successors
+       // throughout the function graph, the cost of removing a function at
+       // position X is proportional to cost * (len(funcG)-X). Therefore, we should
+       // remove high-cost functions last.
+       sort.Slice(funcG, func(i, j int) bool {
+               return funcG[i].cost() < funcG[j].cost()
+       })
+       for _, n := range funcG {
+               // connect each predecessor p of n with each successor s
+               // and drop the function node (don't collect it in G)
+               for p := range n.pred {
+                       // ignore self-cycles
+                       if p != n {
+                               // Each successor s of n becomes a successor of p, and
+                               // each predecessor p of n becomes a predecessor of s.
+                               for s := range n.succ {
+                                       // ignore self-cycles
+                                       if s != n {
+                                               p.succ.add(s)
+                                               s.pred.add(p)
                                        }
-                                       delete(p.succ, n) // remove edge to n
                                }
+                               delete(p.succ, n) // remove edge to n
                        }
-               } else {
-                       // collect non-function nodes
-                       G = append(G, n)
+               }
+               for s := range n.succ {
+                       delete(s.pred, n) // remove edge to n
                }
        }
 
index e0d2e1b07a0bbef3a295b85738ad2ebc3bcae9bc..9a01ccdf7a28bbce7903885bf51e36feaa4ab374 100644 (file)
@@ -33,6 +33,7 @@ func BenchmarkCheck(b *testing.B) {
                filepath.Join("src", "net", "http"),
                filepath.Join("src", "go", "parser"),
                filepath.Join("src", "go", "constant"),
+               filepath.Join("src", "runtime"),
                filepath.Join("src", "go", "internal", "gcimporter"),
        } {
                b.Run(path.Base(p), func(b *testing.B) {