mirror of
https://github.com/golang/go
synced 2024-11-05 16:26:11 -07:00
cmd/compile: make CSE faster
To refine a set of possibly equivalent values, the old CSE algorithm picked one value, compared it against all the others, and made two sets out of the results (the values that match the picked value and the values that didn't). Unfortunately, this leads to O(n^2) behavior. The picked value ends up being equal to no other values, we make size 1 and size n-1 sets, and then recurse on the size n-1 set. Instead, sort the set by the equivalence classes of its arguments. Then we just look for spots in the sorted list where the equivalence classes of the arguments change. This lets us do a multi-way split for O(n lg n) time. This change makes cmpDepth unnecessary. The refinement portion used to call the type comparator. That is unnecessary as the type was already part of the initial partition. Lowers time of 16361 from 8 sec to 3 sec. Lowers time of 15112 from 282 sec to 20 sec. That's kind of unfair, as CL 30257 changed it from 21 sec to 282 sec. But that CL fixed other bad compile times (issue #17127) by large factors, so net still a big win. Fixes #15112 Fixes #16361 Change-Id: I351ce111bae446608968c6d48710eeb6a3d8e527 Reviewed-on: https://go-review.googlesource.com/30354 Reviewed-by: Todd Neal <todd@tneal.org>
This commit is contained in:
parent
bd06d4827a
commit
30088ac9a3
@ -9,10 +9,6 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
cmpDepth = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// cse does common-subexpression elimination on the Function.
|
// cse does common-subexpression elimination on the Function.
|
||||||
// Values are just relinked, nothing is deleted. A subsequent deadcode
|
// Values are just relinked, nothing is deleted. A subsequent deadcode
|
||||||
// pass is required to actually remove duplicate expressions.
|
// pass is required to actually remove duplicate expressions.
|
||||||
@ -60,7 +56,8 @@ func cse(f *Func) {
|
|||||||
valueEqClass[v.ID] = -v.ID
|
valueEqClass[v.ID] = -v.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, e := range partition {
|
var pNum ID = 1
|
||||||
|
for _, e := range partition {
|
||||||
if f.pass.debug > 1 && len(e) > 500 {
|
if f.pass.debug > 1 && len(e) > 500 {
|
||||||
fmt.Printf("CSE.large partition (%d): ", len(e))
|
fmt.Printf("CSE.large partition (%d): ", len(e))
|
||||||
for j := 0; j < 3; j++ {
|
for j := 0; j < 3; j++ {
|
||||||
@ -70,20 +67,22 @@ func cse(f *Func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range e {
|
for _, v := range e {
|
||||||
valueEqClass[v.ID] = ID(i)
|
valueEqClass[v.ID] = pNum
|
||||||
}
|
}
|
||||||
if f.pass.debug > 2 && len(e) > 1 {
|
if f.pass.debug > 2 && len(e) > 1 {
|
||||||
fmt.Printf("CSE.partition #%d:", i)
|
fmt.Printf("CSE.partition #%d:", pNum)
|
||||||
for _, v := range e {
|
for _, v := range e {
|
||||||
fmt.Printf(" %s", v.String())
|
fmt.Printf(" %s", v.String())
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
|
pNum++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find an equivalence class where some members of the class have
|
// Split equivalence classes at points where they have
|
||||||
// non-equivalent arguments. Split the equivalence class appropriately.
|
// non-equivalent arguments. Repeat until we can't find any
|
||||||
// Repeat until we can't find any more splits.
|
// more splits.
|
||||||
|
var splitPoints []int
|
||||||
for {
|
for {
|
||||||
changed := false
|
changed := false
|
||||||
|
|
||||||
@ -91,39 +90,51 @@ func cse(f *Func) {
|
|||||||
// we process new additions as they arrive, avoiding O(n^2) behavior.
|
// we process new additions as they arrive, avoiding O(n^2) behavior.
|
||||||
for i := 0; i < len(partition); i++ {
|
for i := 0; i < len(partition); i++ {
|
||||||
e := partition[i]
|
e := partition[i]
|
||||||
v := e[0]
|
|
||||||
// all values in this equiv class that are not equivalent to v get moved
|
// Sort by eq class of arguments.
|
||||||
// into another equiv class.
|
sort.Sort(partitionByArgClass{e, valueEqClass})
|
||||||
// To avoid allocating while building that equivalence class,
|
|
||||||
// move the values equivalent to v to the beginning of e
|
// Find split points.
|
||||||
// and other values to the end of e.
|
splitPoints = append(splitPoints[:0], 0)
|
||||||
allvals := e
|
for j := 1; j < len(e); j++ {
|
||||||
eqloop:
|
v, w := e[j-1], e[j]
|
||||||
for j := 1; j < len(e); {
|
eqArgs := true
|
||||||
w := e[j]
|
for k, a := range v.Args {
|
||||||
equivalent := true
|
b := w.Args[k]
|
||||||
for i := 0; i < len(v.Args); i++ {
|
if valueEqClass[a.ID] != valueEqClass[b.ID] {
|
||||||
if valueEqClass[v.Args[i].ID] != valueEqClass[w.Args[i].ID] {
|
eqArgs = false
|
||||||
equivalent = false
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equivalent || v.Type.Compare(w.Type) != CMPeq {
|
if !eqArgs {
|
||||||
// w is not equivalent to v.
|
splitPoints = append(splitPoints, j)
|
||||||
// move it to the end and shrink e.
|
|
||||||
e[j], e[len(e)-1] = e[len(e)-1], e[j]
|
|
||||||
e = e[:len(e)-1]
|
|
||||||
valueEqClass[w.ID] = ID(len(partition))
|
|
||||||
changed = true
|
|
||||||
continue eqloop
|
|
||||||
}
|
}
|
||||||
// v and w are equivalent. Keep w in e.
|
|
||||||
j++
|
|
||||||
}
|
}
|
||||||
partition[i] = e
|
if len(splitPoints) == 1 {
|
||||||
if len(e) < len(allvals) {
|
continue // no splits, leave equivalence class alone.
|
||||||
partition = append(partition, allvals[len(e):])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move another equivalence class down in place of e.
|
||||||
|
partition[i] = partition[len(partition)-1]
|
||||||
|
partition = partition[:len(partition)-1]
|
||||||
|
i--
|
||||||
|
|
||||||
|
// Add new equivalence classes for the parts of e we found.
|
||||||
|
splitPoints = append(splitPoints, len(e))
|
||||||
|
for j := 0; j < len(splitPoints)-1; j++ {
|
||||||
|
f := e[splitPoints[j]:splitPoints[j+1]]
|
||||||
|
if len(f) == 1 {
|
||||||
|
// Don't add singletons.
|
||||||
|
valueEqClass[f[0].ID] = -f[0].ID
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, v := range f {
|
||||||
|
valueEqClass[v.ID] = pNum
|
||||||
|
}
|
||||||
|
pNum++
|
||||||
|
partition = append(partition, f)
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !changed {
|
if !changed {
|
||||||
@ -253,7 +264,7 @@ func partitionValues(a []*Value, auxIDs auxmap) []eqclass {
|
|||||||
j := 1
|
j := 1
|
||||||
for ; j < len(a); j++ {
|
for ; j < len(a); j++ {
|
||||||
w := a[j]
|
w := a[j]
|
||||||
if cmpVal(v, w, auxIDs, cmpDepth) != CMPeq {
|
if cmpVal(v, w, auxIDs) != CMPeq {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,7 +285,7 @@ func lt2Cmp(isLt bool) Cmp {
|
|||||||
|
|
||||||
type auxmap map[interface{}]int32
|
type auxmap map[interface{}]int32
|
||||||
|
|
||||||
func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp {
|
func cmpVal(v, w *Value, auxIDs auxmap) Cmp {
|
||||||
// Try to order these comparison by cost (cheaper first)
|
// Try to order these comparison by cost (cheaper first)
|
||||||
if v.Op != w.Op {
|
if v.Op != w.Op {
|
||||||
return lt2Cmp(v.Op < w.Op)
|
return lt2Cmp(v.Op < w.Op)
|
||||||
@ -308,18 +319,6 @@ func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp {
|
|||||||
return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
|
return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
|
||||||
}
|
}
|
||||||
|
|
||||||
if depth > 0 {
|
|
||||||
for i := range v.Args {
|
|
||||||
if v.Args[i] == w.Args[i] {
|
|
||||||
// skip comparing equal args
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ac := cmpVal(v.Args[i], w.Args[i], auxIDs, depth-1); ac != CMPeq {
|
|
||||||
return ac
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMPeq
|
return CMPeq
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +333,7 @@ func (sv sortvalues) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
|
|||||||
func (sv sortvalues) Less(i, j int) bool {
|
func (sv sortvalues) Less(i, j int) bool {
|
||||||
v := sv.a[i]
|
v := sv.a[i]
|
||||||
w := sv.a[j]
|
w := sv.a[j]
|
||||||
if cmp := cmpVal(v, w, sv.auxIDs, cmpDepth); cmp != CMPeq {
|
if cmp := cmpVal(v, w, sv.auxIDs); cmp != CMPeq {
|
||||||
return cmp == CMPlt
|
return cmp == CMPlt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,3 +353,25 @@ func (sv partitionByDom) Less(i, j int) bool {
|
|||||||
w := sv.a[j]
|
w := sv.a[j]
|
||||||
return sv.sdom.domorder(v.Block) < sv.sdom.domorder(w.Block)
|
return sv.sdom.domorder(v.Block) < sv.sdom.domorder(w.Block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type partitionByArgClass struct {
|
||||||
|
a []*Value // array of values
|
||||||
|
eqClass []ID // equivalence class IDs of values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sv partitionByArgClass) Len() int { return len(sv.a) }
|
||||||
|
func (sv partitionByArgClass) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
|
||||||
|
func (sv partitionByArgClass) Less(i, j int) bool {
|
||||||
|
v := sv.a[i]
|
||||||
|
w := sv.a[j]
|
||||||
|
for i, a := range v.Args {
|
||||||
|
b := w.Args[i]
|
||||||
|
if sv.eqClass[a.ID] < sv.eqClass[b.ID] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if sv.eqClass[a.ID] > sv.eqClass[b.ID] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user