]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: speed up cse
authorTodd Neal <todd@tneal.org>
Tue, 23 Feb 2016 23:52:17 +0000 (17:52 -0600)
committerDavid Chase <drchase@google.com>
Wed, 24 Feb 2016 16:57:05 +0000 (16:57 +0000)
Construct better initial partitions by recursively comparing values and
their arguments.  This saves one second on compile of arithConst_ssa.go
(4.3s to 3.3s) and shows a 3-5% increase with compilebench.

name       old time/op     new time/op     delta
Template       266ms ± 3%      253ms ± 4%  -5.08%          (p=0.032 n=5+5)
GoTypes        927ms ± 3%      885ms ± 2%  -4.55%          (p=0.016 n=5+5)
Compiler       3.91s ± 3%      3.73s ± 2%  -4.49%          (p=0.008 n=5+5)
MakeBash       31.6s ± 1%      30.5s ± 3%  -3.51%          (p=0.016 n=5+5)

Change-Id: I6ede31ff459131ccfed69531acfbd06b19837700
Reviewed-on: https://go-review.googlesource.com/19838
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/ssa/cse.go
src/cmd/compile/internal/ssa/type.go

index 44bd87683db978592829fb5420025517bb4e7d88..f7958542aa3f75fbd35d5102a3cb6051a17aa232 100644 (file)
@@ -9,6 +9,10 @@ import (
        "sort"
 )
 
+const (
+       cmpDepth = 4
+)
+
 // cse does common-subexpression elimination on the Function.
 // Values are just relinked, nothing is deleted.  A subsequent deadcode
 // pass is required to actually remove duplicate expressions.
@@ -30,8 +34,12 @@ func cse(f *Func) {
 
        // Make initial coarse partitions by using a subset of the conditions above.
        a := make([]*Value, 0, f.NumValues())
+       auxIDs := auxmap{}
        for _, b := range f.Blocks {
                for _, v := range b.Values {
+                       if auxIDs[v.Aux] == 0 {
+                               auxIDs[v.Aux] = int32(len(auxIDs)) + 1
+                       }
                        if v.Type.IsMemory() {
                                continue // memory values can never cse
                        }
@@ -42,7 +50,7 @@ func cse(f *Func) {
                        a = append(a, v)
                }
        }
-       partition := partitionValues(a)
+       partition := partitionValues(a, auxIDs)
 
        // map from value id back to eqclass id
        valueEqClass := make([]ID, f.NumValues())
@@ -202,8 +210,7 @@ type eqclass []*Value
 // being a sorted by ID list of *Values.  The eqclass slices are
 // backed by the same storage as the input slice.
 // Equivalence classes of size 1 are ignored.
-func partitionValues(a []*Value) []eqclass {
-       auxIDs := map[interface{}]int32{}
+func partitionValues(a []*Value, auxIDs auxmap) []eqclass {
        sort.Sort(sortvalues{a, auxIDs})
 
        var partition []eqclass
@@ -212,30 +219,7 @@ func partitionValues(a []*Value) []eqclass {
                j := 1
                for ; j < len(a); j++ {
                        w := a[j]
-                       rootsDiffer := v.Op != w.Op ||
-                               v.AuxInt != w.AuxInt ||
-                               len(v.Args) != len(w.Args) ||
-                               v.Op == OpPhi && v.Block != w.Block ||
-                               v.Aux != w.Aux
-                       if rootsDiffer ||
-                               len(v.Args) >= 1 && (v.Args[0].Op != w.Args[0].Op ||
-                                       v.Args[0].AuxInt != w.Args[0].AuxInt) ||
-                               len(v.Args) >= 2 && (v.Args[1].Op != w.Args[1].Op ||
-                                       v.Args[1].AuxInt != w.Args[1].AuxInt) ||
-                               v.Type.Compare(w.Type) != CMPeq {
-                               if Debug > 3 {
-                                       fmt.Printf("CSE.partitionValues separates %s from %s, AuxInt=%v, Aux=%v, Type.compare=%v",
-                                               v.LongString(), w.LongString(), v.AuxInt != w.AuxInt, v.Aux != w.Aux, v.Type.Compare(w.Type))
-                                       if !rootsDiffer {
-                                               if len(v.Args) >= 1 {
-                                                       fmt.Printf(", a0Op=%v, a0AuxInt=%v", v.Args[0].Op != w.Args[0].Op, v.Args[0].AuxInt != w.Args[0].AuxInt)
-                                                       if len(v.Args) >= 2 {
-                                                               fmt.Printf(", a1Op=%v, a1AuxInt=%v", v.Args[1].Op != w.Args[1].Op, v.Args[1].AuxInt != w.Args[1].AuxInt)
-                                                       }
-                                               }
-                                       }
-                                       fmt.Printf("\n")
-                               }
+                       if cmpVal(v, w, auxIDs, cmpDepth) != CMPeq {
                                break
                        }
                }
@@ -247,100 +231,73 @@ func partitionValues(a []*Value) []eqclass {
 
        return partition
 }
-
-// Sort values to make the initial partition.
-type sortvalues struct {
-       a      []*Value              // array of values
-       auxIDs map[interface{}]int32 // aux -> aux ID map
+func lt2Cmp(isLt bool) Cmp {
+       if isLt {
+               return CMPlt
+       }
+       return CMPgt
 }
 
-func (sv sortvalues) Len() int      { return len(sv.a) }
-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 {
-       v := sv.a[i]
-       w := sv.a[j]
+type auxmap map[interface{}]int32
+
+func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp {
+       // Try to order these comparison by cost (cheaper first)
        if v.Op != w.Op {
-               return v.Op < w.Op
+               return lt2Cmp(v.Op < w.Op)
        }
        if v.AuxInt != w.AuxInt {
-               return v.AuxInt < w.AuxInt
-       }
-       if v.Aux == nil && w.Aux != nil { // cheap aux check - expensive one below.
-               return true
-       }
-       if v.Aux != nil && w.Aux == nil {
-               return false
+               return lt2Cmp(v.AuxInt < w.AuxInt)
        }
        if len(v.Args) != len(w.Args) {
-               return len(v.Args) < len(w.Args)
+               return lt2Cmp(len(v.Args) < len(w.Args))
        }
-       if v.Op == OpPhi && v.Block.ID != w.Block.ID {
-               return v.Block.ID < w.Block.ID
+       if v.Op == OpPhi && v.Block != w.Block {
+               return lt2Cmp(v.Block.ID < w.Block.ID)
        }
-       if len(v.Args) >= 1 {
-               vOp := v.Args[0].Op
-               wOp := w.Args[0].Op
-               if vOp != wOp {
-                       return vOp < wOp
-               }
 
-               vAuxInt := v.Args[0].AuxInt
-               wAuxInt := w.Args[0].AuxInt
-               if vAuxInt != wAuxInt {
-                       return vAuxInt < wAuxInt
+       if tc := v.Type.Compare(w.Type); tc != CMPeq {
+               return tc
+       }
+
+       if v.Aux != w.Aux {
+               if v.Aux == nil {
+                       return CMPlt
                }
+               if w.Aux == nil {
+                       return CMPgt
+               }
+               return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
+       }
 
-               if len(v.Args) >= 2 {
-                       vOp = v.Args[1].Op
-                       wOp = w.Args[1].Op
-                       if vOp != wOp {
-                               return vOp < wOp
+       if depth > 0 {
+               for i := range v.Args {
+                       if v.Args[i] == w.Args[i] {
+                               // skip comparing equal args
+                               continue
                        }
-
-                       vAuxInt = v.Args[1].AuxInt
-                       wAuxInt = w.Args[1].AuxInt
-                       if vAuxInt != wAuxInt {
-                               return vAuxInt < wAuxInt
+                       if ac := cmpVal(v.Args[i], w.Args[i], auxIDs, depth-1); ac != CMPeq {
+                               return ac
                        }
                }
        }
 
-       // Sort by type, using the ssa.Type Compare method
-       if v.Type != w.Type {
-               c := v.Type.Compare(w.Type)
-               if c != CMPeq {
-                       return c == CMPlt
-               }
-       }
+       return CMPeq
+}
 
-       // Aux fields are interfaces with no comparison
-       // method.  Use a map to number distinct ones,
-       // and use those numbers for comparison.
-       if v.Aux != w.Aux {
-               x := sv.auxIDs[v.Aux]
-               if x == 0 {
-                       x = int32(len(sv.auxIDs)) + 1
-                       sv.auxIDs[v.Aux] = x
-               }
-               y := sv.auxIDs[w.Aux]
-               if y == 0 {
-                       y = int32(len(sv.auxIDs)) + 1
-                       sv.auxIDs[w.Aux] = y
-               }
-               if x != y {
-                       return x < y
-               }
-       }
+// Sort values to make the initial partition.
+type sortvalues struct {
+       a      []*Value // array of values
+       auxIDs auxmap   // aux -> aux ID map
+}
 
-       // TODO(khr): is the above really ok to do?  We're building
-       // the aux->auxID map online as sort is asking about it.  If
-       // sort has some internal randomness, then the numbering might
-       // change from run to run.  That will make the ordering of
-       // partitions random.  It won't break the compiler but may
-       // make it nondeterministic.  We could fix this by computing
-       // the aux->auxID map ahead of time, but the hope is here that
-       // we won't need to compute the mapping for many aux fields
-       // because the values they are in are otherwise unique.
+func (sv sortvalues) Len() int      { return len(sv.a) }
+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 {
+       v := sv.a[i]
+       w := sv.a[j]
+       if cmp := cmpVal(v, w, sv.auxIDs, cmpDepth); cmp != CMPeq {
+               return cmp == CMPlt
+       }
 
        // Sort by value ID last to keep the sort result deterministic.
        return v.ID < w.ID
index afe04fa043c0e9ee545fb723861f574f27145f4c..a23989c82e25c01e4c7ec8c7be32440eff91ba29 100644 (file)
@@ -95,6 +95,9 @@ func (t *CompilerType) Compare(u Type) Cmp {
        if !ok {
                return CMPlt
        }
+       if t == x {
+               return CMPeq
+       }
        // desire fast sorting, not pretty sorting.
        if len(t.Name) == len(x.Name) {
                if t.Name == x.Name {