Skip to content

Commit 3ea7cfa

Browse files
committed
cmd/compile: sort partitions by dom to speed up cse
We do two O(n) scans of all values in an eqclass when computing substitutions for CSE. In unfortunate cases, like those found in #15112, we can have a large eqclass composed of values found in blocks none of whom dominate the other. This leads to O(n^2) behavior. The elements are removed one at a time, with O(n) scans each time. This CL removes the linear scan by sorting the eqclass so that dominant values will be sorted first. As long as we also ensure we don't disturb the sort order, then we no longer need to scan for the maximally dominant value. For the code in issue #15112: Before: real 1m26.094s user 1m30.776s sys 0m1.125s Aefter: real 0m52.099s user 0m56.829s sys 0m1.092s Updates #15112 Change-Id: Ic4f8680ed172e716232436d31963209c146ef850 Reviewed-on: https://go-review.googlesource.com/21981 Reviewed-by: Josh Bleecher Snyder <[email protected]> Run-TryBot: Todd Neal <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 4721ea6 commit 3ea7cfa

File tree

2 files changed

+33
-11
lines changed

2 files changed

+33
-11
lines changed

src/cmd/compile/internal/ssa/cse.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,23 +137,20 @@ func cse(f *Func) {
137137
// if v and w are in the same equivalence class and v dominates w.
138138
rewrite := make([]*Value, f.NumValues())
139139
for _, e := range partition {
140+
sort.Sort(sortbyentry{e, f.sdom})
140141
for len(e) > 1 {
141-
// Find a maximal dominant element in e
142+
// e is sorted by entry value so maximal dominant element should be
143+
// found first in the slice
142144
v := e[0]
143-
for _, w := range e[1:] {
144-
if f.sdom.isAncestorEq(w.Block, v.Block) {
145-
v = w
146-
}
147-
}
148-
145+
e = e[1:]
149146
// Replace all elements of e which v dominates
150147
for i := 0; i < len(e); {
151148
w := e[i]
152-
if w == v {
153-
e, e[i] = e[:len(e)-1], e[len(e)-1]
154-
} else if f.sdom.isAncestorEq(v.Block, w.Block) {
149+
if f.sdom.isAncestorEq(v.Block, w.Block) {
155150
rewrite[w.ID] = v
156-
e, e[i] = e[:len(e)-1], e[len(e)-1]
151+
// retain the sort order
152+
copy(e[i:], e[i+1:])
153+
e = e[:len(e)-1]
157154
} else {
158155
i++
159156
}
@@ -308,3 +305,16 @@ func (sv sortvalues) Less(i, j int) bool {
308305
// Sort by value ID last to keep the sort result deterministic.
309306
return v.ID < w.ID
310307
}
308+
309+
type sortbyentry struct {
310+
a []*Value // array of values
311+
sdom sparseTree
312+
}
313+
314+
func (sv sortbyentry) Len() int { return len(sv.a) }
315+
func (sv sortbyentry) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
316+
func (sv sortbyentry) Less(i, j int) bool {
317+
v := sv.a[i]
318+
w := sv.a[j]
319+
return sv.sdom.maxdomorder(v.Block) < sv.sdom.maxdomorder(w.Block)
320+
}

src/cmd/compile/internal/ssa/sparsetree.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,26 @@ func (t sparseTree) Child(x *Block) *Block {
116116

117117
// isAncestorEq reports whether x is an ancestor of or equal to y.
118118
func (t sparseTree) isAncestorEq(x, y *Block) bool {
119+
if x == y {
120+
return true
121+
}
119122
xx := &t[x.ID]
120123
yy := &t[y.ID]
121124
return xx.entry <= yy.entry && yy.exit <= xx.exit
122125
}
123126

124127
// isAncestor reports whether x is a strict ancestor of y.
125128
func (t sparseTree) isAncestor(x, y *Block) bool {
129+
if x == y {
130+
return false
131+
}
126132
xx := &t[x.ID]
127133
yy := &t[y.ID]
128134
return xx.entry < yy.entry && yy.exit < xx.exit
129135
}
136+
137+
// maxdomorder returns a value to allow a maximal dominator first sort. maxdomorder(x) < maxdomorder(y) is true
138+
// if x may dominate y, and false if x cannot dominate y.
139+
func (t sparseTree) maxdomorder(x *Block) int32 {
140+
return t[x.ID].entry
141+
}

0 commit comments

Comments
 (0)