]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: reuse sparse sets across compiler passes
authorTodd Neal <todd@tneal.org>
Fri, 29 Jan 2016 04:19:46 +0000 (22:19 -0600)
committerTodd Neal <todd@tneal.org>
Sat, 30 Jan 2016 13:57:39 +0000 (13:57 +0000)
Cache sparse sets in the function so they can be reused by subsequent
compiler passes.

benchmark                        old ns/op     new ns/op     delta
BenchmarkDSEPass-8               206945        180022        -13.01%
BenchmarkDSEPassBlock-8          5286103       2614054       -50.55%
BenchmarkCSEPass-8               1790277       1790655       +0.02%
BenchmarkCSEPassBlock-8          18083588      18112771      +0.16%
BenchmarkDeadcodePass-8          59837         41375         -30.85%
BenchmarkDeadcodePassBlock-8     1651575       511169        -69.05%
BenchmarkMultiPass-8             531529        427506        -19.57%
BenchmarkMultiPassBlock-8        7033496       4487814       -36.19%

benchmark                        old allocs     new allocs     delta
BenchmarkDSEPass-8               11             4              -63.64%
BenchmarkDSEPassBlock-8          599            120            -79.97%
BenchmarkCSEPass-8               18             18             +0.00%
BenchmarkCSEPassBlock-8          2700           2700           +0.00%
BenchmarkDeadcodePass-8          4              3              -25.00%
BenchmarkDeadcodePassBlock-8     30             9              -70.00%
BenchmarkMultiPass-8             24             20             -16.67%
BenchmarkMultiPassBlock-8        1800           1000           -44.44%

benchmark                        old bytes     new bytes     delta
BenchmarkDSEPass-8               221367        142           -99.94%
BenchmarkDSEPassBlock-8          3695207       3846          -99.90%
BenchmarkCSEPass-8               303328        303328        +0.00%
BenchmarkCSEPassBlock-8          5006400       5006400       +0.00%
BenchmarkDeadcodePass-8          84232         10506         -87.53%
BenchmarkDeadcodePassBlock-8     1274940       163680        -87.16%
BenchmarkMultiPass-8             608674        313834        -48.44%
BenchmarkMultiPassBlock-8        9906001       5003450       -49.49%

Change-Id: Ib1fa58c7f494b374d1a4bb9cffbc2c48377b59d3
Reviewed-on: https://go-review.googlesource.com/19100
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/ssa/deadcode.go
src/cmd/compile/internal/ssa/deadstore.go
src/cmd/compile/internal/ssa/func.go
src/cmd/compile/internal/ssa/layout.go
src/cmd/compile/internal/ssa/passbm_test.go [new file with mode: 0644]
src/cmd/compile/internal/ssa/regalloc.go
src/cmd/compile/internal/ssa/sparseset.go
src/cmd/compile/internal/ssa/stackalloc.go

index 80e14900142f7afcb79b73ab5c36aafaf3bc6441..87244a6248818f6f485e05ca8d3ec6f13d054a6a 100644 (file)
@@ -134,7 +134,8 @@ func deadcode(f *Func) {
        live := liveValues(f, reachable)
 
        // Remove dead & duplicate entries from namedValues map.
-       s := newSparseSet(f.NumValues())
+       s := f.newSparseSet(f.NumValues())
+       defer f.retSparseSet(s)
        i := 0
        for _, name := range f.Names {
                j := 0
index 89f7504341fdf2441dcc844b157e7a05eff607e8..bad0e0096fd9f41521a02ee832a47d7d6fec69b8 100644 (file)
@@ -10,9 +10,12 @@ package ssa
 // This implementation only works within a basic block.  TODO: use something more global.
 func dse(f *Func) {
        var stores []*Value
-       loadUse := newSparseSet(f.NumValues())
-       storeUse := newSparseSet(f.NumValues())
-       shadowed := newSparseSet(f.NumValues())
+       loadUse := f.newSparseSet(f.NumValues())
+       defer f.retSparseSet(loadUse)
+       storeUse := f.newSparseSet(f.NumValues())
+       defer f.retSparseSet(storeUse)
+       shadowed := f.newSparseSet(f.NumValues())
+       defer f.retSparseSet(shadowed)
        for _, b := range f.Blocks {
                // Find all the stores in this block.  Categorize their uses:
                //  loadUse contains stores which are used by a subsequent load.
index a28484010d0be1a9d8c90fc54413ed651e80020b..9da390904d8ac3d4ae5c284f4d94975e0cc0fb1a 100644 (file)
@@ -31,6 +31,8 @@ type Func struct {
 
        freeValues *Value // free Values linked by argstorage[0].  All other fields except ID are 0/nil.
        freeBlocks *Block // free Blocks linked by succstorage[0].  All other fields except ID are 0/nil.
+
+       scrSparse []*sparseSet // sparse sets to be re-used.
 }
 
 // NumBlocks returns an integer larger than the id of any Block in the Func.
@@ -43,6 +45,29 @@ func (f *Func) NumValues() int {
        return f.vid.num()
 }
 
+// newSparseSet returns a sparse set that can store at least up to n integers.
+func (f *Func) newSparseSet(n int) *sparseSet {
+       for i, scr := range f.scrSparse {
+               if scr != nil && scr.cap() >= n {
+                       f.scrSparse[i] = nil
+                       scr.clear()
+                       return scr
+               }
+       }
+       return newSparseSet(n)
+}
+
+// retSparseSet returns a sparse set to the function's cache to be reused by f.newSparseSet.
+func (f *Func) retSparseSet(ss *sparseSet) {
+       for i, scr := range f.scrSparse {
+               if scr == nil {
+                       f.scrSparse[i] = ss
+                       return
+               }
+       }
+       f.scrSparse = append(f.scrSparse, ss)
+}
+
 // newValue allocates a new Value with the given fields and places it at the end of b.Values.
 func (f *Func) newValue(op Op, t Type, b *Block, line int32) *Value {
        var v *Value
index 7e865f948efac49b0afad148953c6cc5ea7bf70b..8dd4b6597981e5798f51a2f27a81f918202300ac 100644 (file)
@@ -12,8 +12,10 @@ func layout(f *Func) {
        scheduled := make([]bool, f.NumBlocks())
        idToBlock := make([]*Block, f.NumBlocks())
        indegree := make([]int, f.NumBlocks())
-       posdegree := newSparseSet(f.NumBlocks())  // blocks with positive remaining degree
-       zerodegree := newSparseSet(f.NumBlocks()) // blocks with zero remaining degree
+       posdegree := f.newSparseSet(f.NumBlocks()) // blocks with positive remaining degree
+       defer f.retSparseSet(posdegree)
+       zerodegree := f.newSparseSet(f.NumBlocks()) // blocks with zero remaining degree
+       defer f.retSparseSet(zerodegree)
 
        // Initialize indegree of each block
        for _, b := range f.Blocks {
diff --git a/src/cmd/compile/internal/ssa/passbm_test.go b/src/cmd/compile/internal/ssa/passbm_test.go
new file mode 100644 (file)
index 0000000..9b11ff1
--- /dev/null
@@ -0,0 +1,101 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package ssa
+
+import (
+       "fmt"
+       "testing"
+)
+
+const (
+       blockCount = 1000
+       passCount  = 15000
+)
+
+type passFunc func(*Func)
+
+func BenchmarkDSEPass(b *testing.B)           { benchFnPass(b, dse, blockCount, genFunction) }
+func BenchmarkDSEPassBlock(b *testing.B)      { benchFnBlock(b, dse, genFunction) }
+func BenchmarkCSEPass(b *testing.B)           { benchFnPass(b, cse, blockCount, genFunction) }
+func BenchmarkCSEPassBlock(b *testing.B)      { benchFnBlock(b, cse, genFunction) }
+func BenchmarkDeadcodePass(b *testing.B)      { benchFnPass(b, deadcode, blockCount, genFunction) }
+func BenchmarkDeadcodePassBlock(b *testing.B) { benchFnBlock(b, deadcode, genFunction) }
+
+func multi(f *Func) {
+       cse(f)
+       dse(f)
+       deadcode(f)
+}
+func BenchmarkMultiPass(b *testing.B)      { benchFnPass(b, multi, blockCount, genFunction) }
+func BenchmarkMultiPassBlock(b *testing.B) { benchFnBlock(b, multi, genFunction) }
+
+// benchFnPass runs passFunc b.N times across a single function.
+func benchFnPass(b *testing.B, fn passFunc, size int, bg blockGen) {
+       b.ReportAllocs()
+       c := NewConfig("amd64", DummyFrontend{b}, nil, true)
+       fun := Fun(c, "entry", bg(size)...)
+
+       CheckFunc(fun.f)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               fn(fun.f)
+               b.StopTimer()
+               CheckFunc(fun.f)
+               b.StartTimer()
+       }
+}
+
+// benchFnPass runs passFunc across a function with b.N blocks.
+func benchFnBlock(b *testing.B, fn passFunc, bg blockGen) {
+       b.ReportAllocs()
+       c := NewConfig("amd64", DummyFrontend{b}, nil, true)
+       fun := Fun(c, "entry", bg(b.N)...)
+
+       CheckFunc(fun.f)
+       b.ResetTimer()
+       for i := 0; i < passCount; i++ {
+               fn(fun.f)
+       }
+       b.StopTimer()
+}
+
+func genFunction(size int) []bloc {
+       var blocs []bloc
+       elemType := &TypeImpl{Size_: 8, Name: "testtype"}
+       ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing
+
+       valn := func(s string, m, n int) string { return fmt.Sprintf("%s%d-%d", s, m, n) }
+       blocs = append(blocs,
+               Bloc("entry",
+                       Valu(valn("store", 0, 4), OpArg, TypeMem, 0, ".mem"),
+                       Valu("sb", OpSB, TypeInvalid, 0, nil),
+                       Goto(blockn(1)),
+               ),
+       )
+       for i := 1; i < size+1; i++ {
+               blocs = append(blocs, Bloc(blockn(i),
+                       Valu(valn("v", i, 0), OpConstBool, TypeBool, 1, nil),
+                       Valu(valn("addr", i, 1), OpAddr, ptrType, 0, nil, "sb"),
+                       Valu(valn("addr", i, 2), OpAddr, ptrType, 0, nil, "sb"),
+                       Valu(valn("addr", i, 3), OpAddr, ptrType, 0, nil, "sb"),
+                       Valu(valn("zero", i, 1), OpZero, TypeMem, 8, nil, valn("addr", i, 3),
+                               valn("store", i-1, 4)),
+                       Valu(valn("store", i, 1), OpStore, TypeMem, 0, nil, valn("addr", i, 1),
+                               valn("v", i, 0), valn("zero", i, 1)),
+                       Valu(valn("store", i, 2), OpStore, TypeMem, 0, nil, valn("addr", i, 2),
+                               valn("v", i, 0), valn("store", i, 1)),
+                       Valu(valn("store", i, 3), OpStore, TypeMem, 0, nil, valn("addr", i, 1),
+                               valn("v", i, 0), valn("store", i, 2)),
+                       Valu(valn("store", i, 4), OpStore, TypeMem, 0, nil, valn("addr", i, 3),
+                               valn("v", i, 0), valn("store", i, 3)),
+                       Goto(blockn(i+1))))
+       }
+
+       blocs = append(blocs,
+               Bloc(blockn(size+1), Goto("exit")),
+               Bloc("exit", Exit("store0-4")),
+       )
+
+       return blocs
+}
index 61f694355e8843396d22093d96602b8c353d65db..2d88850999eed67c02d69f0ac0bf469f11c8d59d 100644 (file)
@@ -559,7 +559,8 @@ func (s *regAllocState) compatRegs(t Type) regMask {
 }
 
 func (s *regAllocState) regalloc(f *Func) {
-       liveSet := newSparseSet(f.NumValues())
+       liveSet := f.newSparseSet(f.NumValues())
+       defer f.retSparseSet(liveSet)
        var oldSched []*Value
        var phis []*Value
        var phiRegs []register
index b79aee84977894c242d5a1ca71340853578006c6..66bebf139e66bfe2f6d755c45844374e9f0e0a0c 100644 (file)
@@ -18,6 +18,10 @@ func newSparseSet(n int) *sparseSet {
        return &sparseSet{nil, make([]int, n)}
 }
 
+func (s *sparseSet) cap() int {
+       return len(s.sparse)
+}
+
 func (s *sparseSet) size() int {
        return len(s.dense)
 }
index 797a6b05e6055503cb20b853f0d5dd729433fb29..0e6cae09242d628abd14c0d548c871f1c0191e30 100644 (file)
@@ -182,8 +182,10 @@ func (s *stackAllocState) stackalloc() {
 func (s *stackAllocState) computeLive(spillLive [][]ID) {
        s.live = make([][]ID, s.f.NumBlocks())
        var phis []*Value
-       live := newSparseSet(s.f.NumValues())
-       t := newSparseSet(s.f.NumValues())
+       live := s.f.newSparseSet(s.f.NumValues())
+       defer s.f.retSparseSet(live)
+       t := s.f.newSparseSet(s.f.NumValues())
+       defer s.f.retSparseSet(t)
 
        // Instead of iterating over f.Blocks, iterate over their postordering.
        // Liveness information flows backward, so starting at the end
@@ -271,7 +273,8 @@ func (f *Func) setHome(v *Value, loc Location) {
 func (s *stackAllocState) buildInterferenceGraph() {
        f := s.f
        s.interfere = make([][]ID, f.NumValues())
-       live := newSparseSet(f.NumValues())
+       live := f.newSparseSet(f.NumValues())
+       defer f.retSparseSet(live)
        for _, b := range f.Blocks {
                // Propagate liveness backwards to the start of the block.
                // Two values interfere if one is defined while the other is live.