aboutsummaryrefslogtreecommitdiff
path: root/test/escape2.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-01-19 22:59:58 +0300
committerDmitry Vyukov <dvyukov@google.com>2015-01-29 13:07:30 +0000
commit0e80b2e082db784c55ec26ed997226e11c4f5f46 (patch)
treebfff8fa8769704d59cae819363ea5717e227c882 /test/escape2.go
parente16ed2870786c427fd9c265bc656cb01738fa63e (diff)
downloadgo-0e80b2e082db784c55ec26ed997226e11c4f5f46.tar.gz
go-0e80b2e082db784c55ec26ed997226e11c4f5f46.zip
cmd/gc: capture variables by value
Language specification says that variables are captured by reference. And that is what gc compiler does. However, in lots of cases it is possible to capture variables by value under the hood without affecting visible behavior of programs. For example, consider the following typical pattern: func (o *Obj) requestMany(urls []string) []Result { wg := new(sync.WaitGroup) wg.Add(len(urls)) res := make([]Result, len(urls)) for i := range urls { i := i go func() { res[i] = o.requestOne(urls[i]) wg.Done() }() } wg.Wait() return res } Currently o, wg, res, and i are captured by reference causing 3+len(urls) allocations (e.g. PPARAM o is promoted to PPARAMREF and moved to heap). But all of them can be captured by value without changing behavior. This change implements simple strategy for capturing by value: if a captured variable is not addrtaken and never assigned to, then it is captured by value (it is effectively const). This simple strategy turned out to be very effective: ~80% of all captures in std lib are turned into value captures. The remaining 20% are mostly in defers and non-escaping closures, that is, they do not cause allocations anyway. benchmark old allocs new allocs delta BenchmarkCompressedZipGarbage 153 126 -17.65% BenchmarkEncodeDigitsSpeed1e4 91 69 -24.18% BenchmarkEncodeDigitsSpeed1e5 178 129 -27.53% BenchmarkEncodeDigitsSpeed1e6 1510 1051 -30.40% BenchmarkEncodeDigitsDefault1e4 100 75 -25.00% BenchmarkEncodeDigitsDefault1e5 193 139 -27.98% BenchmarkEncodeDigitsDefault1e6 1420 985 -30.63% BenchmarkEncodeDigitsCompress1e4 100 75 -25.00% BenchmarkEncodeDigitsCompress1e5 193 139 -27.98% BenchmarkEncodeDigitsCompress1e6 1420 985 -30.63% BenchmarkEncodeTwainSpeed1e4 109 81 -25.69% BenchmarkEncodeTwainSpeed1e5 211 151 -28.44% BenchmarkEncodeTwainSpeed1e6 1588 1097 -30.92% BenchmarkEncodeTwainDefault1e4 103 77 -25.24% BenchmarkEncodeTwainDefault1e5 199 143 -28.14% BenchmarkEncodeTwainDefault1e6 1324 917 -30.74% BenchmarkEncodeTwainCompress1e4 103 77 -25.24% BenchmarkEncodeTwainCompress1e5 190 137 -27.89% BenchmarkEncodeTwainCompress1e6 1327 919 -30.75% BenchmarkConcurrentDBExec 16223 16220 -0.02% BenchmarkConcurrentStmtQuery 17687 16182 -8.51% BenchmarkConcurrentStmtExec 5191 5186 -0.10% BenchmarkConcurrentTxQuery 17665 17661 -0.02% BenchmarkConcurrentTxExec 15154 15150 -0.03% BenchmarkConcurrentTxStmtQuery 17661 16157 -8.52% BenchmarkConcurrentTxStmtExec 3677 3673 -0.11% BenchmarkConcurrentRandom 14000 13614 -2.76% BenchmarkManyConcurrentQueries 25 22 -12.00% BenchmarkDecodeComplex128Slice 318 252 -20.75% BenchmarkDecodeFloat64Slice 318 252 -20.75% BenchmarkDecodeInt32Slice 318 252 -20.75% BenchmarkDecodeStringSlice 2318 2252 -2.85% BenchmarkDecode 11 8 -27.27% BenchmarkEncodeGray 64 56 -12.50% BenchmarkEncodeNRGBOpaque 64 56 -12.50% BenchmarkEncodeNRGBA 67 58 -13.43% BenchmarkEncodePaletted 68 60 -11.76% BenchmarkEncodeRGBOpaque 64 56 -12.50% BenchmarkGoLookupIP 153 139 -9.15% BenchmarkGoLookupIPNoSuchHost 508 466 -8.27% BenchmarkGoLookupIPWithBrokenNameServer 245 226 -7.76% BenchmarkClientServer 62 59 -4.84% BenchmarkClientServerParallel4 62 59 -4.84% BenchmarkClientServerParallel64 62 59 -4.84% BenchmarkClientServerParallelTLS4 79 76 -3.80% BenchmarkClientServerParallelTLS64 112 109 -2.68% BenchmarkCreateGoroutinesCapture 10 6 -40.00% BenchmarkAfterFunc 1006 1005 -0.10% Fixes #6632. Change-Id: I0cd51e4d356331d7f3c5f447669080cd19b0d2ca Reviewed-on: https://go-review.googlesource.com/3166 Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'test/escape2.go')
-rw-r--r--test/escape2.go84
1 files changed, 68 insertions, 16 deletions
diff --git a/test/escape2.go b/test/escape2.go
index 1523d9f1ff..8c50277e9d 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -203,9 +203,17 @@ func (b *Bar2) LeakSelf2() { // ERROR "leaking param: b"
}
func foo21() func() int {
+ x := 42
+ return func() int { // ERROR "func literal escapes to heap"
+ return x
+ }
+}
+
+func foo21a() func() int {
x := 42 // ERROR "moved to heap: x"
return func() int { // ERROR "func literal escapes to heap"
- return x // ERROR "&x escapes to heap"
+ x++ // ERROR "&x escapes to heap"
+ return x
}
}
@@ -216,24 +224,31 @@ func foo22() int {
}()
}
-func foo23(x int) func() int { // ERROR "moved to heap: x"
+func foo23(x int) func() int {
return func() int { // ERROR "func literal escapes to heap"
- return x // ERROR "&x escapes to heap"
+ return x
}
}
-func foo23a(x int) func() int { // ERROR "moved to heap: x"
+func foo23a(x int) func() int {
f := func() int { // ERROR "func literal escapes to heap"
- return x // ERROR "&x escapes to heap"
+ return x
}
return f
}
-func foo23b(x int) *(func() int) { // ERROR "moved to heap: x"
- f := func() int { return x } // ERROR "moved to heap: f" "func literal escapes to heap" "&x escapes to heap"
+func foo23b(x int) *(func() int) {
+ f := func() int { return x } // ERROR "moved to heap: f" "func literal escapes to heap"
return &f // ERROR "&f escapes to heap"
}
+func foo23c(x int) func() int { // ERROR "moved to heap: x"
+ return func() int { // ERROR "func literal escapes to heap"
+ x++ // ERROR "&x escapes to heap"
+ return x
+ }
+}
+
func foo24(x int) int {
return func() int { // ERROR "func literal does not escape"
return x
@@ -525,10 +540,22 @@ func foo72b() [10]*int {
func foo73() {
s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape"
for _, v := range s {
+ vv := v
+ // actually just escapes its scope
+ defer func() { // ERROR "func literal escapes to heap"
+ println(vv)
+ }()
+ }
+}
+
+func foo731() {
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape"
+ for _, v := range s {
vv := v // ERROR "moved to heap: vv"
// actually just escapes its scope
defer func() { // ERROR "func literal escapes to heap"
- println(vv) // ERROR "&vv escapes to heap"
+ vv = 42 // ERROR "&vv escapes to heap"
+ println(vv)
}()
}
}
@@ -536,10 +563,23 @@ func foo73() {
func foo74() {
s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape"
for _, v := range s {
+ vv := v
+ // actually just escapes its scope
+ fn := func() { // ERROR "func literal escapes to heap"
+ println(vv)
+ }
+ defer fn()
+ }
+}
+
+func foo74a() {
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape"
+ for _, v := range s {
vv := v // ERROR "moved to heap: vv"
// actually just escapes its scope
fn := func() { // ERROR "func literal escapes to heap"
- println(vv) // ERROR "&vv escapes to heap"
+ vv += 1 // ERROR "&vv escapes to heap"
+ println(vv)
}
defer fn()
}
@@ -550,10 +590,22 @@ func foo74b() {
var array [3]func()
s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape"
for i, v := range s {
+ vv := v
+ // actually just escapes its scope
+ array[i] = func() { // ERROR "func literal escapes to heap"
+ println(vv)
+ }
+ }
+}
+
+func foo74c() {
+ var array [3]func()
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape"
+ for i, v := range s {
vv := v // ERROR "moved to heap: vv"
// actually just escapes its scope
array[i] = func() { // ERROR "func literal escapes to heap"
- println(vv) // ERROR "&vv escapes to heap"
+ println(&vv) // ERROR "&vv escapes to heap" "&vv does not escape"
}
}
}
@@ -1213,9 +1265,9 @@ func foo134() {
func foo135() {
var i int // ERROR "moved to heap: i"
- p := &i // ERROR "&i escapes to heap" "moved to heap: p"
+ p := &i // ERROR "&i escapes to heap"
go func() { // ERROR "func literal escapes to heap"
- q := p // ERROR "&p escapes to heap"
+ q := p
func() { // ERROR "func literal does not escape"
r := q
_ = r
@@ -1225,9 +1277,9 @@ func foo135() {
func foo136() {
var i int // ERROR "moved to heap: i"
- p := &i // ERROR "&i escapes to heap" "moved to heap: p"
+ p := &i // ERROR "&i escapes to heap"
go func() { // ERROR "func literal escapes to heap"
- q := p // ERROR "&p escapes to heap" "leaking closure reference p"
+ q := p // ERROR "leaking closure reference p"
func() { // ERROR "func literal does not escape"
r := q // ERROR "leaking closure reference q"
px = r
@@ -1239,9 +1291,9 @@ func foo137() {
var i int // ERROR "moved to heap: i"
p := &i // ERROR "&i escapes to heap"
func() { // ERROR "func literal does not escape"
- q := p // ERROR "leaking closure reference p" "moved to heap: q"
+ q := p // ERROR "leaking closure reference p"
go func() { // ERROR "func literal escapes to heap"
- r := q // ERROR "&q escapes to heap"
+ r := q
_ = r
}()
}()