Skip to content

Commit c9ccdf1

Browse files
committed
cmd/compile: make deadcode pass cheaper
The deadcode pass runs a lot. I'd like it to run even more. This change adds dedicated storage for deadcode to ssa.Cache. In addition to being a nice win now, it makes deadcode easier to add other places in the future. name old time/op new time/op delta Template 210ms ± 3% 209ms ± 2% ~ (p=0.951 n=93+95) Unicode 92.2ms ± 3% 93.0ms ± 3% +0.87% (p=0.000 n=94+94) GoTypes 739ms ± 2% 733ms ± 2% -0.84% (p=0.000 n=92+94) Compiler 3.51s ± 2% 3.49s ± 2% -0.57% (p=0.000 n=94+91) SSA 9.80s ± 2% 9.75s ± 2% -0.57% (p=0.000 n=95+92) Flate 132ms ± 2% 132ms ± 3% ~ (p=0.165 n=94+98) GoParser 160ms ± 3% 159ms ± 3% -0.42% (p=0.005 n=96+94) Reflect 446ms ± 4% 442ms ± 4% -0.91% (p=0.000 n=95+98) Tar 186ms ± 3% 186ms ± 2% ~ (p=0.221 n=94+97) XML 252ms ± 2% 250ms ± 2% -0.55% (p=0.000 n=95+94) [Geo mean] 430ms 429ms -0.34% name old user-time/op new user-time/op delta Template 256ms ± 3% 257ms ± 3% ~ (p=0.521 n=94+98) Unicode 120ms ± 9% 121ms ± 9% ~ (p=0.074 n=99+100) GoTypes 935ms ± 3% 935ms ± 2% ~ (p=0.574 n=82+96) Compiler 4.56s ± 1% 4.55s ± 2% ~ (p=0.247 n=88+90) SSA 13.6s ± 2% 13.6s ± 1% ~ (p=0.277 n=94+95) Flate 155ms ± 3% 156ms ± 3% ~ (p=0.181 n=95+100) GoParser 193ms ± 8% 184ms ± 6% -4.39% (p=0.000 n=100+89) Reflect 549ms ± 3% 552ms ± 3% +0.45% (p=0.036 n=94+96) Tar 230ms ± 4% 230ms ± 4% ~ (p=0.670 n=97+99) XML 315ms ± 5% 309ms ±12% -2.05% (p=0.000 n=99+99) [Geo mean] 540ms 538ms -0.47% name old alloc/op new alloc/op delta Template 40.3MB ± 0% 38.9MB ± 0% -3.36% (p=0.008 n=5+5) Unicode 28.6MB ± 0% 28.4MB ± 0% -0.90% (p=0.008 n=5+5) GoTypes 137MB ± 0% 132MB ± 0% -3.65% (p=0.008 n=5+5) Compiler 637MB ± 0% 609MB ± 0% -4.40% (p=0.008 n=5+5) SSA 2.19GB ± 0% 2.07GB ± 0% -5.63% (p=0.008 n=5+5) Flate 25.0MB ± 0% 24.1MB ± 0% -3.80% (p=0.008 n=5+5) GoParser 30.0MB ± 0% 29.1MB ± 0% -3.17% (p=0.008 n=5+5) Reflect 87.1MB ± 0% 84.4MB ± 0% -3.05% (p=0.008 n=5+5) Tar 37.3MB ± 0% 36.0MB ± 0% -3.31% (p=0.008 n=5+5) XML 49.8MB ± 0% 48.0MB ± 0% -3.69% (p=0.008 n=5+5) [Geo mean] 87.6MB 84.6MB -3.50% name old allocs/op new allocs/op delta Template 387k ± 0% 380k ± 0% -1.76% (p=0.008 n=5+5) Unicode 342k ± 0% 341k ± 0% -0.31% (p=0.008 n=5+5) GoTypes 1.39M ± 0% 1.37M ± 0% -1.64% (p=0.008 n=5+5) Compiler 5.68M ± 0% 5.60M ± 0% -1.41% (p=0.008 n=5+5) SSA 17.1M ± 0% 16.8M ± 0% -1.49% (p=0.008 n=5+5) Flate 240k ± 0% 236k ± 0% -1.99% (p=0.008 n=5+5) GoParser 309k ± 0% 304k ± 0% -1.57% (p=0.008 n=5+5) Reflect 1.01M ± 0% 0.99M ± 0% -2.69% (p=0.008 n=5+5) Tar 360k ± 0% 353k ± 0% -1.91% (p=0.008 n=5+5) XML 447k ± 0% 441k ± 0% -1.26% (p=0.008 n=5+5) [Geo mean] 858k 844k -1.60% Fixes #15306 Change-Id: I9f558adb911efddead3865542fe2ca71f66fe1da Reviewed-on: https://go-review.googlesource.com/c/go/+/166718 Run-TryBot: Josh Bleecher Snyder <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 810809e commit c9ccdf1

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

src/cmd/compile/internal/ssa/cache.go

+21
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ type Cache struct {
2525
scrSparseSet []*sparseSet // scratch sparse sets to be re-used.
2626
scrSparseMap []*sparseMap // scratch sparse maps to be re-used.
2727
scrPoset []*poset // scratch poset to be reused
28+
// deadcode contains reusable slices specifically for the deadcode pass.
29+
// It gets special treatment because of the frequency with which it is run.
30+
deadcode struct {
31+
liveOrderStmts []*Value
32+
live []bool
33+
q []*Value
34+
}
2835

2936
ValueToProgAfter []*obj.Prog
3037
debugState debugState
@@ -49,4 +56,18 @@ func (c *Cache) Reset() {
4956
xl[i] = nil
5057
}
5158

59+
// liveOrderStmts gets used multiple times during compilation of a function.
60+
// We don't know where the high water mark was, so reslice to cap and search.
61+
c.deadcode.liveOrderStmts = c.deadcode.liveOrderStmts[:cap(c.deadcode.liveOrderStmts)]
62+
no := sort.Search(len(c.deadcode.liveOrderStmts), func(i int) bool { return c.deadcode.liveOrderStmts[i] == nil })
63+
xo := c.deadcode.liveOrderStmts[:no]
64+
for i := range xo {
65+
xo[i] = nil
66+
}
67+
c.deadcode.q = c.deadcode.q[:cap(c.deadcode.q)]
68+
nq := sort.Search(len(c.deadcode.q), func(i int) bool { return c.deadcode.q[i] == nil })
69+
xq := c.deadcode.q[:nq]
70+
for i := range xq {
71+
xq[i] = nil
72+
}
5273
}

src/cmd/compile/internal/ssa/deadcode.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ import (
99
)
1010

1111
// findlive returns the reachable blocks and live values in f.
12+
// The caller should call f.retDeadcodeLive(live) when it is done with it.
1213
func findlive(f *Func) (reachable []bool, live []bool) {
1314
reachable = ReachableBlocks(f)
14-
live, _ = liveValues(f, reachable)
15+
var order []*Value
16+
live, order = liveValues(f, reachable)
17+
f.retDeadcodeLiveOrderStmts(order)
1518
return
1619
}
1720

@@ -48,8 +51,21 @@ func ReachableBlocks(f *Func) []bool {
4851
// to be statements in reversed data flow order.
4952
// The second result is used to help conserve statement boundaries for debugging.
5053
// reachable is a map from block ID to whether the block is reachable.
54+
// The caller should call f.retDeadcodeLive(live) and f.retDeadcodeLiveOrderStmts(liveOrderStmts)
55+
// when they are done with the return values.
5156
func liveValues(f *Func, reachable []bool) (live []bool, liveOrderStmts []*Value) {
52-
live = make([]bool, f.NumValues())
57+
live = f.newDeadcodeLive()
58+
if cap(live) < f.NumValues() {
59+
live = make([]bool, f.NumValues())
60+
} else {
61+
live = live[:f.NumValues()]
62+
for i := range live {
63+
live[i] = false
64+
}
65+
}
66+
67+
liveOrderStmts = f.newDeadcodeLiveOrderStmts()
68+
liveOrderStmts = liveOrderStmts[:0]
5369

5470
// After regalloc, consider all values to be live.
5571
// See the comment at the top of regalloc.go and in deadcode for details.
@@ -61,7 +77,8 @@ func liveValues(f *Func, reachable []bool) (live []bool, liveOrderStmts []*Value
6177
}
6278

6379
// Find all live values
64-
q := make([]*Value, 0, 64) // stack-like worklist of unscanned values
80+
q := f.Cache.deadcode.q[:0]
81+
defer func() { f.Cache.deadcode.q = q }()
6582

6683
// Starting set: all control values of reachable blocks are live.
6784
// Calls are live (because callee can observe the memory state).
@@ -163,6 +180,8 @@ func deadcode(f *Func) {
163180

164181
// Find live values.
165182
live, order := liveValues(f, reachable)
183+
defer f.retDeadcodeLive(live)
184+
defer f.retDeadcodeLiveOrderStmts(order)
166185

167186
// Remove dead & duplicate entries from namedValues map.
168187
s := f.newSparseSet(f.NumValues())

src/cmd/compile/internal/ssa/func.go

+27
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,33 @@ func (f *Func) retPoset(po *poset) {
153153
f.Cache.scrPoset = append(f.Cache.scrPoset, po)
154154
}
155155

156+
// newDeadcodeLive returns a slice for the
157+
// deadcode pass to use to indicate which values are live.
158+
func (f *Func) newDeadcodeLive() []bool {
159+
r := f.Cache.deadcode.live
160+
f.Cache.deadcode.live = nil
161+
return r
162+
}
163+
164+
// retDeadcodeLive returns a deadcode live value slice for re-use.
165+
func (f *Func) retDeadcodeLive(live []bool) {
166+
f.Cache.deadcode.live = live
167+
}
168+
169+
// newDeadcodeLiveOrderStmts returns a slice for the
170+
// deadcode pass to use to indicate which values
171+
// need special treatment for statement boundaries.
172+
func (f *Func) newDeadcodeLiveOrderStmts() []*Value {
173+
r := f.Cache.deadcode.liveOrderStmts
174+
f.Cache.deadcode.liveOrderStmts = nil
175+
return r
176+
}
177+
178+
// retDeadcodeLiveOrderStmts returns a deadcode liveOrderStmts slice for re-use.
179+
func (f *Func) retDeadcodeLiveOrderStmts(liveOrderStmts []*Value) {
180+
f.Cache.deadcode.liveOrderStmts = liveOrderStmts
181+
}
182+
156183
// newValue allocates a new Value with the given fields and places it at the end of b.Values.
157184
func (f *Func) newValue(op Op, t *types.Type, b *Block, pos src.XPos) *Value {
158185
var v *Value

src/cmd/compile/internal/ssa/print.go

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func (p stringFuncPrinter) named(n LocalSlot, vals []*Value) {
8383

8484
func fprintFunc(p funcPrinter, f *Func) {
8585
reachable, live := findlive(f)
86+
defer f.retDeadcodeLive(live)
8687
p.header(f)
8788
printed := make([]bool, f.NumValues())
8889
for _, b := range f.Blocks {

0 commit comments

Comments
 (0)