Skip to content

Commit

Permalink
go/callgraph/vta: optimize scc computation
Browse files Browse the repository at this point in the history
Optimizes how sccs are internally computed by reducing the
amount of maps created and map lookups performed. Between
4-10% faster on benchmarks of callgraph construction.

Updates golang/go#57357

Change-Id: I0ecd92c32358a56d1cae67beb352ec15b7b4367e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/460435
Run-TryBot: Tim King <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
gopls-CI: kokoro <[email protected]>
Reviewed-by: Zvonimir Pavlinovic <[email protected]>
  • Loading branch information
timothy-king committed Jan 9, 2023
1 parent 2eb6138 commit 67baca6
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 29 deletions.
11 changes: 5 additions & 6 deletions go/callgraph/callgraph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@ import (
// Current results were on an i7 macbook on go version devel go1.20-2730.
// Number of nodes, edges, and reachable function are expected to vary between
// go versions. Timing results are expected to vary between machines.
//
// BenchmarkStatic-12 53 ms/op 6 MB/op 12113 nodes 37355 edges 1522 reachable
// BenchmarkCHA-12 86 ms/op 16 MB/op 12113 nodes 131717 edges 7640 reachable
// BenchmarkRTA-12 110 ms/op 12 MB/op 6566 nodes 42291 edges 5099 reachable
// BenchmarkPTA-12 1427 ms/op 600 MB/op 8714 nodes 28244 edges 4184 reachable
// BenchmarkVTA-12 603 ms/op 87 MB/op 12112 nodes 44857 edges 4918 reachable
// BenchmarkVTA2-12 774 ms/op 115 MB/op 4918 nodes 20247 edges 3841 reachable
// BenchmarkVTA3-12 928 ms/op 134 MB/op 3841 nodes 16502 edges 3542 reachable
// BenchmarkVTAAlt-12 395 ms/op 61 MB/op 7640 nodes 29629 edges 4257 reachable
// BenchmarkVTAAlt2-12 556 ms/op 82 MB/op 4257 nodes 18057 edges 3586 reachable
// BenchmarkVTA-12 562 ms/op 78 MB/op 12112 nodes 44857 edges 4918 reachable
// BenchmarkVTA2-12 743 ms/op 103 MB/op 4918 nodes 20247 edges 3841 reachable
// BenchmarkVTA3-12 837 ms/op 119 MB/op 3841 nodes 16502 edges 3542 reachable
// BenchmarkVTAAlt-12 356 ms/op 56 MB/op 7640 nodes 29629 edges 4257 reachable
// BenchmarkVTAAlt2-12 490 ms/op 75 MB/op 4257 nodes 18057 edges 3586 reachable
//
// Note:
// * Static is unsound and may miss real edges.
Expand Down
45 changes: 22 additions & 23 deletions go/callgraph/vta/propagation.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,61 +20,60 @@ import (
// with ids X and Y s.t. X < Y, Y comes before X in the topological order.
func scc(g vtaGraph) (map[node]int, int) {
// standard data structures used by Tarjan's algorithm.
var index uint64
type state struct {
index int
lowLink int
onStack bool
}
states := make(map[node]*state, len(g))
var stack []node
indexMap := make(map[node]uint64)
lowLink := make(map[node]uint64)
onStack := make(map[node]bool)

nodeToSccID := make(map[node]int)
nodeToSccID := make(map[node]int, len(g))
sccID := 0

var doSCC func(node)
doSCC = func(n node) {
indexMap[n] = index
lowLink[n] = index
index = index + 1
onStack[n] = true
index := len(states)
ns := &state{index: index, lowLink: index, onStack: true}
states[n] = ns
stack = append(stack, n)

for s := range g[n] {
if _, ok := indexMap[s]; !ok {
if ss, visited := states[s]; !visited {
// Analyze successor s that has not been visited yet.
doSCC(s)
lowLink[n] = min(lowLink[n], lowLink[s])
} else if onStack[s] {
ss = states[s]
ns.lowLink = min(ns.lowLink, ss.lowLink)
} else if ss.onStack {
// The successor is on the stack, meaning it has to be
// in the current SCC.
lowLink[n] = min(lowLink[n], indexMap[s])
ns.lowLink = min(ns.lowLink, ss.index)
}
}

// if n is a root node, pop the stack and generate a new SCC.
if lowLink[n] == indexMap[n] {
for {
w := stack[len(stack)-1]
if ns.lowLink == index {
var w node
for w != n {
w = stack[len(stack)-1]
stack = stack[:len(stack)-1]
onStack[w] = false
states[w].onStack = false
nodeToSccID[w] = sccID
if w == n {
break
}
}
sccID++
}
}

index = 0
for n := range g {
if _, ok := indexMap[n]; !ok {
if _, visited := states[n]; !visited {
doSCC(n)
}
}

return nodeToSccID, sccID
}

func min(x, y uint64) uint64 {
func min(x, y int) int {
if x < y {
return x
}
Expand Down

0 comments on commit 67baca6

Please sign in to comment.