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  }