From: Todd Neal Date: Wed, 13 Apr 2016 12:51:46 +0000 (-0400) Subject: cmd/compile: sort partitions by dom to speed up cse X-Git-Tag: go1.7beta1~703 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=3ea7cfabbb0549d62d524e4ad30cb464af250fde;p=gostls13.git 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 Run-TryBot: Todd Neal TryBot-Result: Gobot Gobot --- diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go index c12d51e50c..76db9d5467 100644 --- a/src/cmd/compile/internal/ssa/cse.go +++ b/src/cmd/compile/internal/ssa/cse.go @@ -137,23 +137,20 @@ func cse(f *Func) { // if v and w are in the same equivalence class and v dominates w. rewrite := make([]*Value, f.NumValues()) for _, e := range partition { + sort.Sort(sortbyentry{e, f.sdom}) for len(e) > 1 { - // Find a maximal dominant element in e + // e is sorted by entry value so maximal dominant element should be + // found first in the slice v := e[0] - for _, w := range e[1:] { - if f.sdom.isAncestorEq(w.Block, v.Block) { - v = w - } - } - + e = e[1:] // Replace all elements of e which v dominates for i := 0; i < len(e); { w := e[i] - if w == v { - e, e[i] = e[:len(e)-1], e[len(e)-1] - } else if f.sdom.isAncestorEq(v.Block, w.Block) { + if f.sdom.isAncestorEq(v.Block, w.Block) { rewrite[w.ID] = v - e, e[i] = e[:len(e)-1], e[len(e)-1] + // retain the sort order + copy(e[i:], e[i+1:]) + e = e[:len(e)-1] } else { i++ } @@ -308,3 +305,16 @@ func (sv sortvalues) Less(i, j int) bool { // Sort by value ID last to keep the sort result deterministic. return v.ID < w.ID } + +type sortbyentry struct { + a []*Value // array of values + sdom sparseTree +} + +func (sv sortbyentry) Len() int { return len(sv.a) } +func (sv sortbyentry) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] } +func (sv sortbyentry) Less(i, j int) bool { + v := sv.a[i] + w := sv.a[j] + return sv.sdom.maxdomorder(v.Block) < sv.sdom.maxdomorder(w.Block) +} diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go index cae91e7ddb..45c7897496 100644 --- a/src/cmd/compile/internal/ssa/sparsetree.go +++ b/src/cmd/compile/internal/ssa/sparsetree.go @@ -116,6 +116,9 @@ func (t sparseTree) Child(x *Block) *Block { // isAncestorEq reports whether x is an ancestor of or equal to y. func (t sparseTree) isAncestorEq(x, y *Block) bool { + if x == y { + return true + } xx := &t[x.ID] yy := &t[y.ID] return xx.entry <= yy.entry && yy.exit <= xx.exit @@ -123,7 +126,16 @@ func (t sparseTree) isAncestorEq(x, y *Block) bool { // isAncestor reports whether x is a strict ancestor of y. func (t sparseTree) isAncestor(x, y *Block) bool { + if x == y { + return false + } xx := &t[x.ID] yy := &t[y.ID] return xx.entry < yy.entry && yy.exit < xx.exit } + +// maxdomorder returns a value to allow a maximal dominator first sort. maxdomorder(x) < maxdomorder(y) is true +// if x may dominate y, and false if x cannot dominate y. +func (t sparseTree) maxdomorder(x *Block) int32 { + return t[x.ID].entry +}