github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/flow/control_flow_bench_test.go (about) 1 // Copyright ©2017 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package flow 6 7 import ( 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "math" 12 "os" 13 "path/filepath" 14 "strings" 15 "testing" 16 17 "golang.org/x/exp/rand" 18 19 "github.com/jingcheng-WU/gonum/graph" 20 "github.com/jingcheng-WU/gonum/graph/encoding" 21 "github.com/jingcheng-WU/gonum/graph/encoding/dot" 22 "github.com/jingcheng-WU/gonum/graph/graphs/gen" 23 "github.com/jingcheng-WU/gonum/graph/iterator" 24 "github.com/jingcheng-WU/gonum/graph/simple" 25 "github.com/jingcheng-WU/gonum/graph/topo" 26 ) 27 28 var slta = flag.Bool("slta", false, "specify DominatorsSLT benchmark") 29 30 func BenchmarkDominators(b *testing.B) { 31 testdata := filepath.FromSlash("./testdata/flow") 32 33 fis, err := ioutil.ReadDir(testdata) 34 if err != nil { 35 if os.IsNotExist(err) { 36 b.Skipf("no control flow testdata: %v", err) 37 } 38 b.Fatalf("failed to open control flow testdata: %v", err) 39 } 40 for _, fi := range fis { 41 name := fi.Name() 42 ext := filepath.Ext(name) 43 if ext != ".dot" { 44 continue 45 } 46 test := name[:len(name)-len(ext)] 47 48 data, err := ioutil.ReadFile(filepath.Join(testdata, name)) 49 if err != nil { 50 b.Errorf("failed to open control flow case: %v", err) 51 continue 52 } 53 g := &labeled{DirectedGraph: simple.NewDirectedGraph()} 54 err = dot.Unmarshal(data, g) 55 if err != nil { 56 b.Errorf("failed to unmarshal graph data: %v", err) 57 continue 58 } 59 want := g.root 60 if want == nil { 61 b.Error("no entry node label for graph") 62 continue 63 } 64 65 if *slta { 66 b.Run(test, func(b *testing.B) { 67 for i := 0; i < b.N; i++ { 68 d := DominatorsSLT(g.root, g) 69 if got := d.Root(); got.ID() != want.ID() { 70 b.Fatalf("unexpected root node: got:%d want:%d", got.ID(), want.ID()) 71 } 72 } 73 }) 74 } else { 75 b.Run(test, func(b *testing.B) { 76 for i := 0; i < b.N; i++ { 77 d := Dominators(g.root, g) 78 if got := d.Root(); got.ID() != want.ID() { 79 b.Fatalf("unexpected root node: got:%d want:%d", got.ID(), want.ID()) 80 } 81 } 82 }) 83 } 84 } 85 } 86 87 type labeled struct { 88 *simple.DirectedGraph 89 90 root *node 91 } 92 93 func (g *labeled) NewNode() graph.Node { 94 return &node{Node: g.DirectedGraph.NewNode(), g: g} 95 } 96 97 func (g *labeled) SetEdge(e graph.Edge) { 98 if e.To().ID() == e.From().ID() { 99 // Do not attempt to add self edges. 100 return 101 } 102 g.DirectedGraph.SetEdge(e) 103 } 104 105 type node struct { 106 graph.Node 107 name string 108 g *labeled 109 } 110 111 func (n *node) SetDOTID(id string) { 112 n.name = id 113 } 114 115 func (n *node) SetAttribute(attr encoding.Attribute) error { 116 if attr.Key != "label" { 117 return nil 118 } 119 switch attr.Value { 120 default: 121 if attr.Value != `"{%0}"` && !strings.HasPrefix(attr.Value, `"{%0|`) { 122 return nil 123 } 124 fallthrough 125 case "entry", "root": 126 if n.g.root != nil { 127 return fmt.Errorf("set root for graph with existing root: old=%q new=%q", n.g.root.name, n.name) 128 } 129 n.g.root = n 130 } 131 return nil 132 } 133 134 func BenchmarkRandomGraphDominators(b *testing.B) { 135 tests := []struct { 136 name string 137 g func() *simple.DirectedGraph 138 }{ 139 {name: "gnm-n=1e3-m=1e3", g: gnm(1e3, 1e3)}, 140 {name: "gnm-n=1e3-m=3e3", g: gnm(1e3, 3e3)}, 141 {name: "gnm-n=1e3-m=1e4", g: gnm(1e3, 1e4)}, 142 {name: "gnm-n=1e3-m=3e4", g: gnm(1e3, 3e4)}, 143 144 {name: "gnm-n=1e4-m=1e4", g: gnm(1e4, 1e4)}, 145 {name: "gnm-n=1e4-m=3e4", g: gnm(1e4, 3e4)}, 146 {name: "gnm-n=1e4-m=1e5", g: gnm(1e4, 1e5)}, 147 {name: "gnm-n=1e4-m=3e5", g: gnm(1e4, 3e5)}, 148 149 {name: "gnm-n=1e5-m=1e5", g: gnm(1e5, 1e5)}, 150 {name: "gnm-n=1e5-m=3e5", g: gnm(1e5, 3e5)}, 151 {name: "gnm-n=1e5-m=1e6", g: gnm(1e5, 1e6)}, 152 {name: "gnm-n=1e5-m=3e6", g: gnm(1e5, 3e6)}, 153 154 {name: "gnm-n=1e6-m=1e6", g: gnm(1e6, 1e6)}, 155 {name: "gnm-n=1e6-m=3e6", g: gnm(1e6, 3e6)}, 156 {name: "gnm-n=1e6-m=1e7", g: gnm(1e6, 1e7)}, 157 {name: "gnm-n=1e6-m=3e7", g: gnm(1e6, 3e7)}, 158 159 {name: "dup-n=1e3-d=0.8-a=0.1", g: duplication(1e3, 0.8, 0.1, math.NaN())}, 160 {name: "dup-n=1e3-d=0.5-a=0.2", g: duplication(1e3, 0.5, 0.2, math.NaN())}, 161 162 {name: "dup-n=1e4-d=0.8-a=0.1", g: duplication(1e4, 0.8, 0.1, math.NaN())}, 163 {name: "dup-n=1e4-d=0.5-a=0.2", g: duplication(1e4, 0.5, 0.2, math.NaN())}, 164 165 {name: "dup-n=1e5-d=0.8-a=0.1", g: duplication(1e5, 0.8, 0.1, math.NaN())}, 166 {name: "dup-n=1e5-d=0.5-a=0.2", g: duplication(1e5, 0.5, 0.2, math.NaN())}, 167 } 168 169 for _, test := range tests { 170 rnd := rand.New(rand.NewSource(1)) 171 g := test.g() 172 173 // Guess a maximally expensive entry to the graph. 174 sort, err := topo.Sort(g) 175 root := sort[0] 176 if root == nil { 177 // If we did not get a node in the first position 178 // then there must be an unorderable set of nodes 179 // in the first position of the error. Pick one 180 // of the nodes at random. 181 unordered := err.(topo.Unorderable) 182 root = unordered[0][rnd.Intn(len(unordered[0]))] 183 } 184 if root == nil { 185 b.Error("no entry node label for graph") 186 continue 187 } 188 189 if len(sort) > 1 { 190 // Ensure that the graph has a complete path 191 // through the sorted nodes. 192 193 // unordered will only be accessed if there is 194 // a sort element that is nil, in which case 195 // unordered will contain a set of nodes from 196 // an SCC. 197 unordered, _ := err.(topo.Unorderable) 198 199 var ui int 200 for i, v := range sort[1:] { 201 u := sort[i] 202 if u == nil { 203 u = unordered[ui][rnd.Intn(len(unordered[ui]))] 204 ui++ 205 } 206 if v == nil { 207 v = unordered[ui][rnd.Intn(len(unordered[ui]))] 208 } 209 if !g.HasEdgeFromTo(u.ID(), v.ID()) { 210 g.SetEdge(g.NewEdge(u, v)) 211 } 212 } 213 } 214 215 b.Run(test.name, func(b *testing.B) { 216 for i := 0; i < b.N; i++ { 217 d := Dominators(root, g) 218 if got := d.Root(); got.ID() != root.ID() { 219 b.Fatalf("unexpected root node: got:%d want:%d", got.ID(), root.ID()) 220 } 221 } 222 }) 223 } 224 } 225 226 // gnm returns a directed G(n,m) Erdõs-Rényi graph. 227 func gnm(n, m int) func() *simple.DirectedGraph { 228 return func() *simple.DirectedGraph { 229 dg := simple.NewDirectedGraph() 230 err := gen.Gnm(dg, n, m, rand.New(rand.NewSource(1))) 231 if err != nil { 232 panic(err) 233 } 234 return dg 235 } 236 } 237 238 // duplication returns an edge-induced directed subgraph of a 239 // duplication graph. 240 func duplication(n int, delta, alpha, sigma float64) func() *simple.DirectedGraph { 241 return func() *simple.DirectedGraph { 242 g := undirected{simple.NewDirectedGraph()} 243 rnd := rand.New(rand.NewSource(1)) 244 err := gen.Duplication(g, n, delta, alpha, sigma, rnd) 245 if err != nil { 246 panic(err) 247 } 248 for _, e := range graph.EdgesOf(g.Edges()) { 249 if rnd.Intn(2) == 0 { 250 g.RemoveEdge(e.From().ID(), e.To().ID()) 251 } 252 } 253 return g.DirectedGraph 254 } 255 } 256 257 type undirected struct { 258 *simple.DirectedGraph 259 } 260 261 func (g undirected) From(id int64) graph.Nodes { 262 return iterator.NewOrderedNodes(append( 263 graph.NodesOf(g.DirectedGraph.From(id)), 264 graph.NodesOf(g.DirectedGraph.To(id))...)) 265 } 266 267 func (g undirected) HasEdgeBetween(xid, yid int64) bool { 268 return g.DirectedGraph.HasEdgeFromTo(xid, yid) 269 } 270 271 func (g undirected) EdgeBetween(xid, yid int64) graph.Edge { 272 return g.DirectedGraph.Edge(xid, yid) 273 } 274 275 func (g undirected) SetEdge(e graph.Edge) { 276 g.DirectedGraph.SetEdge(e) 277 g.DirectedGraph.SetEdge(g.DirectedGraph.NewEdge(e.To(), e.From())) 278 }