mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.ssa] cmd/compile: reuse sparse sets across compiler passes
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>
This commit is contained in:
parent
88b230eaa6
commit
f962f33035
8 changed files with 150 additions and 10 deletions
|
|
@ -134,7 +134,8 @@ func deadcode(f *Func) {
|
||||||
live := liveValues(f, reachable)
|
live := liveValues(f, reachable)
|
||||||
|
|
||||||
// Remove dead & duplicate entries from namedValues map.
|
// Remove dead & duplicate entries from namedValues map.
|
||||||
s := newSparseSet(f.NumValues())
|
s := f.newSparseSet(f.NumValues())
|
||||||
|
defer f.retSparseSet(s)
|
||||||
i := 0
|
i := 0
|
||||||
for _, name := range f.Names {
|
for _, name := range f.Names {
|
||||||
j := 0
|
j := 0
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,12 @@ package ssa
|
||||||
// This implementation only works within a basic block. TODO: use something more global.
|
// This implementation only works within a basic block. TODO: use something more global.
|
||||||
func dse(f *Func) {
|
func dse(f *Func) {
|
||||||
var stores []*Value
|
var stores []*Value
|
||||||
loadUse := newSparseSet(f.NumValues())
|
loadUse := f.newSparseSet(f.NumValues())
|
||||||
storeUse := newSparseSet(f.NumValues())
|
defer f.retSparseSet(loadUse)
|
||||||
shadowed := newSparseSet(f.NumValues())
|
storeUse := f.newSparseSet(f.NumValues())
|
||||||
|
defer f.retSparseSet(storeUse)
|
||||||
|
shadowed := f.newSparseSet(f.NumValues())
|
||||||
|
defer f.retSparseSet(shadowed)
|
||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
// Find all the stores in this block. Categorize their uses:
|
// Find all the stores in this block. Categorize their uses:
|
||||||
// loadUse contains stores which are used by a subsequent load.
|
// loadUse contains stores which are used by a subsequent load.
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ type Func struct {
|
||||||
|
|
||||||
freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil.
|
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.
|
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.
|
// 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()
|
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.
|
// 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 {
|
func (f *Func) newValue(op Op, t Type, b *Block, line int32) *Value {
|
||||||
var v *Value
|
var v *Value
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,10 @@ func layout(f *Func) {
|
||||||
scheduled := make([]bool, f.NumBlocks())
|
scheduled := make([]bool, f.NumBlocks())
|
||||||
idToBlock := make([]*Block, f.NumBlocks())
|
idToBlock := make([]*Block, f.NumBlocks())
|
||||||
indegree := make([]int, f.NumBlocks())
|
indegree := make([]int, f.NumBlocks())
|
||||||
posdegree := newSparseSet(f.NumBlocks()) // blocks with positive remaining degree
|
posdegree := f.newSparseSet(f.NumBlocks()) // blocks with positive remaining degree
|
||||||
zerodegree := newSparseSet(f.NumBlocks()) // blocks with zero 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
|
// Initialize indegree of each block
|
||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
|
|
|
||||||
101
src/cmd/compile/internal/ssa/passbm_test.go
Normal file
101
src/cmd/compile/internal/ssa/passbm_test.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -559,7 +559,8 @@ func (s *regAllocState) compatRegs(t Type) regMask {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *regAllocState) regalloc(f *Func) {
|
func (s *regAllocState) regalloc(f *Func) {
|
||||||
liveSet := newSparseSet(f.NumValues())
|
liveSet := f.newSparseSet(f.NumValues())
|
||||||
|
defer f.retSparseSet(liveSet)
|
||||||
var oldSched []*Value
|
var oldSched []*Value
|
||||||
var phis []*Value
|
var phis []*Value
|
||||||
var phiRegs []register
|
var phiRegs []register
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ func newSparseSet(n int) *sparseSet {
|
||||||
return &sparseSet{nil, make([]int, n)}
|
return &sparseSet{nil, make([]int, n)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sparseSet) cap() int {
|
||||||
|
return len(s.sparse)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sparseSet) size() int {
|
func (s *sparseSet) size() int {
|
||||||
return len(s.dense)
|
return len(s.dense)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,8 +182,10 @@ func (s *stackAllocState) stackalloc() {
|
||||||
func (s *stackAllocState) computeLive(spillLive [][]ID) {
|
func (s *stackAllocState) computeLive(spillLive [][]ID) {
|
||||||
s.live = make([][]ID, s.f.NumBlocks())
|
s.live = make([][]ID, s.f.NumBlocks())
|
||||||
var phis []*Value
|
var phis []*Value
|
||||||
live := newSparseSet(s.f.NumValues())
|
live := s.f.newSparseSet(s.f.NumValues())
|
||||||
t := 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.
|
// Instead of iterating over f.Blocks, iterate over their postordering.
|
||||||
// Liveness information flows backward, so starting at the end
|
// 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() {
|
func (s *stackAllocState) buildInterferenceGraph() {
|
||||||
f := s.f
|
f := s.f
|
||||||
s.interfere = make([][]ID, f.NumValues())
|
s.interfere = make([][]ID, f.NumValues())
|
||||||
live := newSparseSet(f.NumValues())
|
live := f.newSparseSet(f.NumValues())
|
||||||
|
defer f.retSparseSet(live)
|
||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
// Propagate liveness backwards to the start of the block.
|
// Propagate liveness backwards to the start of the block.
|
||||||
// Two values interfere if one is defined while the other is live.
|
// Two values interfere if one is defined while the other is live.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue