Commit ca5417b8 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

cmd/compile: reduce some SSA garbage

It's pretty hard to get reliable CPU numbers, even with 50 runs on an
otherwise-idle physical Linux machine, but the garbage reduction
numbers are nice. To get useful time/op numbers, I modified
compilebench to report user CPU time instead of wall time:

name       old time/op     new time/op     delta
Template       547ms ± 6%      557ms ± 5%   +1.80%        (p=0.001 n=49+49)
Unicode        360ms ± 9%      365ms ± 6%     ~           (p=0.094 n=50+45)
GoTypes        1.84s ± 3%      1.82s ± 3%   -1.50%        (p=0.000 n=50+49)
Compiler       9.19s ± 2%      9.02s ± 2%   -1.87%        (p=0.000 n=45+50)

name       old alloc/op    new alloc/op    delta
Template      63.3MB ± 0%     59.1MB ± 0%   -6.72%        (p=0.000 n=50+50)
Unicode       43.1MB ± 0%     42.9MB ± 0%   -0.47%        (p=0.000 n=50+49)
GoTypes        220MB ± 0%      200MB ± 0%   -9.00%        (p=0.000 n=50+50)
Compiler      1.00GB ± 0%     0.89GB ± 0%  -10.09%        (p=0.000 n=50+49)

name       old allocs/op   new allocs/op   delta
Template        681k ± 0%       680k ± 0%   -0.16%        (p=0.000 n=50+48)
Unicode         541k ± 0%       541k ± 0%   -0.02%        (p=0.011 n=48+50)
GoTypes        2.08M ± 0%      2.08M ± 0%   -0.19%        (p=0.000 n=48+50)
Compiler       9.24M ± 0%      9.23M ± 0%   -0.11%        (p=0.000 n=50+50)

Change-Id: I1fac4ebf85a1783e3289c3ffb1ed365442837643
Reviewed-on: https://go-review.googlesource.com/20995
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarDave Cheney <dave@cheney.net>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 0659cf69
...@@ -36,6 +36,10 @@ type Config struct { ...@@ -36,6 +36,10 @@ type Config struct {
values [2000]Value values [2000]Value
blocks [200]Block blocks [200]Block
// Reusable stackAllocState.
// See stackalloc.go's {new,put}StackAllocState.
stackAllocState *stackAllocState
domblockstore []ID // scratch space for computing dominators domblockstore []ID // scratch space for computing dominators
scrSparse []*sparseSet // scratch sparse sets to be re-used. scrSparse []*sparseSet // scratch sparse sets to be re-used.
} }
......
...@@ -9,10 +9,51 @@ package ssa ...@@ -9,10 +9,51 @@ package ssa
import "fmt" import "fmt"
type stackAllocState struct { type stackAllocState struct {
f *Func f *Func
// live is the output of stackalloc.
// live[b.id] = live values at the end of block b.
live [][]ID
// The following slices are reused across multiple users
// of stackAllocState.
values []stackValState values []stackValState
live [][]ID // live[b.id] = live values at the end of block b.
interfere [][]ID // interfere[v.id] = values that interfere with v. interfere [][]ID // interfere[v.id] = values that interfere with v.
names []LocalSlot
slots []int
used []bool
}
func newStackAllocState(f *Func) *stackAllocState {
s := f.Config.stackAllocState
if s == nil {
return new(stackAllocState)
}
if s.f != nil {
f.Config.Fatalf(0, "newStackAllocState called without previous free")
}
return s
}
func putStackAllocState(s *stackAllocState) {
for i := range s.values {
s.values[i] = stackValState{}
}
for i := range s.interfere {
s.interfere[i] = nil
}
for i := range s.names {
s.names[i] = LocalSlot{}
}
for i := range s.slots {
s.slots[i] = 0
}
for i := range s.used {
s.used[i] = false
}
s.f.Config.stackAllocState = s
s.f = nil
s.live = nil
} }
type stackValState struct { type stackValState struct {
...@@ -29,8 +70,10 @@ func stackalloc(f *Func, spillLive [][]ID) [][]ID { ...@@ -29,8 +70,10 @@ func stackalloc(f *Func, spillLive [][]ID) [][]ID {
fmt.Println("before stackalloc") fmt.Println("before stackalloc")
fmt.Println(f.String()) fmt.Println(f.String())
} }
var s stackAllocState s := newStackAllocState(f)
s.init(f, spillLive) s.init(f, spillLive)
defer putStackAllocState(s)
s.stackalloc() s.stackalloc()
return s.live return s.live
} }
...@@ -39,7 +82,11 @@ func (s *stackAllocState) init(f *Func, spillLive [][]ID) { ...@@ -39,7 +82,11 @@ func (s *stackAllocState) init(f *Func, spillLive [][]ID) {
s.f = f s.f = f
// Initialize value information. // Initialize value information.
s.values = make([]stackValState, f.NumValues()) if n := f.NumValues(); cap(s.values) >= n {
s.values = s.values[:n]
} else {
s.values = make([]stackValState, n)
}
for _, b := range f.Blocks { for _, b := range f.Blocks {
for _, v := range b.Values { for _, v := range b.Values {
s.values[v.ID].typ = v.Type s.values[v.ID].typ = v.Type
...@@ -66,7 +113,12 @@ func (s *stackAllocState) stackalloc() { ...@@ -66,7 +113,12 @@ func (s *stackAllocState) stackalloc() {
// Build map from values to their names, if any. // Build map from values to their names, if any.
// A value may be associated with more than one name (e.g. after // A value may be associated with more than one name (e.g. after
// the assignment i=j). This step picks one name per value arbitrarily. // the assignment i=j). This step picks one name per value arbitrarily.
names := make([]LocalSlot, f.NumValues()) if n := f.NumValues(); cap(s.names) >= n {
s.names = s.names[:n]
} else {
s.names = make([]LocalSlot, n)
}
names := s.names
for _, name := range f.Names { for _, name := range f.Names {
// Note: not "range f.NamedValues" above, because // Note: not "range f.NamedValues" above, because
// that would be nondeterministic. // that would be nondeterministic.
...@@ -96,13 +148,25 @@ func (s *stackAllocState) stackalloc() { ...@@ -96,13 +148,25 @@ func (s *stackAllocState) stackalloc() {
// Each time we assign a stack slot to a value v, we remember // Each time we assign a stack slot to a value v, we remember
// the slot we used via an index into locations[v.Type]. // the slot we used via an index into locations[v.Type].
slots := make([]int, f.NumValues()) slots := s.slots
if n := f.NumValues(); cap(slots) >= n {
slots = slots[:n]
} else {
slots = make([]int, n)
s.slots = slots
}
for i := f.NumValues() - 1; i >= 0; i-- { for i := f.NumValues() - 1; i >= 0; i-- {
slots[i] = -1 slots[i] = -1
} }
// Pick a stack slot for each value needing one. // Pick a stack slot for each value needing one.
used := make([]bool, f.NumValues()) var used []bool
if n := f.NumValues(); cap(s.used) >= n {
used = s.used[:n]
} else {
used = make([]bool, n)
s.used = used
}
for _, b := range f.Blocks { for _, b := range f.Blocks {
for _, v := range b.Values { for _, v := range b.Values {
if !s.values[v.ID].needSlot { if !s.values[v.ID].needSlot {
...@@ -270,7 +334,11 @@ func (f *Func) setHome(v *Value, loc Location) { ...@@ -270,7 +334,11 @@ 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()) if n := f.NumValues(); cap(s.interfere) >= n {
s.interfere = s.interfere[:n]
} else {
s.interfere = make([][]ID, n)
}
live := f.newSparseSet(f.NumValues()) live := f.newSparseSet(f.NumValues())
defer f.retSparseSet(live) defer f.retSparseSet(live)
for _, b := range f.Blocks { for _, b := range f.Blocks {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment