Walking the stack by frames is ~3x more expensive
than not, and since it didn't end up being precise,
there is not enough benefit to outweigh the cost.
This is the conservative choice: this CL makes the
stack scanning behavior the same as it was in Go 1.1.
Add benchmarks to package runtime so that we have
them when we re-enable this feature during the
Go 1.3 development.
benchmark old ns/op new ns/op delta
BenchmarkGoroutineSelect
3194909 1272092 -60.18%
BenchmarkGoroutineBlocking
3120282 866366 -72.23%
BenchmarkGoroutineForRange
3256179 939902 -71.13%
BenchmarkGoroutineIdle
2005571 482982 -75.92%
The Go 1 benchmarks, just to add more data.
As far as I can tell the changes are mainly noise.
benchmark old ns/op new ns/op delta
BenchmarkBinaryTree17
4409403046 4414734932 +0.12%
BenchmarkFannkuch11
3407708965 3378306120 -0.86%
BenchmarkFmtFprintfEmpty 100 99 -0.60%
BenchmarkFmtFprintfString 242 239 -1.24%
BenchmarkFmtFprintfInt 204 206 +0.98%
BenchmarkFmtFprintfIntInt 320 316 -1.25%
BenchmarkFmtFprintfPrefixedInt 295 299 +1.36%
BenchmarkFmtFprintfFloat 442 435 -1.58%
BenchmarkFmtManyArgs 1246 1216 -2.41%
BenchmarkGobDecode
10186951 10051210 -1.33%
BenchmarkGobEncode
16504381 16445650 -0.36%
BenchmarkGzip
447030885 447056865 +0.01%
BenchmarkGunzip
111056154 111696305 +0.58%
BenchmarkHTTPClientServer 89973 93040 +3.41%
BenchmarkJSONEncode
28174182 27933893 -0.85%
BenchmarkJSONDecode
106353777 110443817 +3.85%
BenchmarkMandelbrot200
4822289 4806083 -0.34%
BenchmarkGoParse
6102436 6142734 +0.66%
BenchmarkRegexpMatchEasy0_32 133 132 -0.75%
BenchmarkRegexpMatchEasy0_1K 372 373 +0.27%
BenchmarkRegexpMatchEasy1_32 113 111 -1.77%
BenchmarkRegexpMatchEasy1_1K 964 940 -2.49%
BenchmarkRegexpMatchMedium_32 202 205 +1.49%
BenchmarkRegexpMatchMedium_1K 68862 68858 -0.01%
BenchmarkRegexpMatchHard_32 3480 3407 -2.10%
BenchmarkRegexpMatchHard_1K 108255 112614 +4.03%
BenchmarkRevcomp
751393035 743929976 -0.99%
BenchmarkTemplate
139637041 135402220 -3.03%
BenchmarkTimeParse 479 475 -0.84%
BenchmarkTimeFormat 460 466 +1.30%
benchmark old MB/s new MB/s speedup
BenchmarkGobDecode 75.34 76.36 1.01x
BenchmarkGobEncode 46.50 46.67 1.00x
BenchmarkGzip 43.41 43.41 1.00x
BenchmarkGunzip 174.73 173.73 0.99x
BenchmarkJSONEncode 68.87 69.47 1.01x
BenchmarkJSONDecode 18.25 17.57 0.96x
BenchmarkGoParse 9.49 9.43 0.99x
BenchmarkRegexpMatchEasy0_32 239.58 241.74 1.01x
BenchmarkRegexpMatchEasy0_1K 2749.74 2738.00 1.00x
BenchmarkRegexpMatchEasy1_32 282.49 286.32 1.01x
BenchmarkRegexpMatchEasy1_1K 1062.00 1088.96 1.03x
BenchmarkRegexpMatchMedium_32 4.93 4.86 0.99x
BenchmarkRegexpMatchMedium_1K 14.87 14.87 1.00x
BenchmarkRegexpMatchHard_32 9.19 9.39 1.02x
BenchmarkRegexpMatchHard_1K 9.46 9.09 0.96x
BenchmarkRevcomp 338.26 341.65 1.01x
BenchmarkTemplate 13.90 14.33 1.03x
Fixes #6482.
R=golang-dev, dave, r
CC=golang-dev
https://golang.org/cl/
14257043
package runtime_test
import (
+ "flag"
. "runtime"
"testing"
+ "time"
"unsafe"
)
}
mallocSink = x
}
+
+var n = flag.Int("n", 1000, "number of goroutines")
+
+func BenchmarkGoroutineSelect(b *testing.B) {
+ quit := make(chan struct{})
+ read := func(ch chan struct{}) {
+ for {
+ select {
+ case _, ok := <-ch:
+ if !ok {
+ return
+ }
+ case <-quit:
+ return
+ }
+ }
+ }
+ benchHelper(b, *n, read)
+}
+
+func BenchmarkGoroutineBlocking(b *testing.B) {
+ read := func(ch chan struct{}) {
+ for {
+ if _, ok := <-ch; !ok {
+ return
+ }
+ }
+ }
+ benchHelper(b, *n, read)
+}
+
+func BenchmarkGoroutineForRange(b *testing.B) {
+ read := func(ch chan struct{}) {
+ for _ = range ch {
+ }
+ }
+ benchHelper(b, *n, read)
+}
+
+func benchHelper(b *testing.B, n int, read func(chan struct{})) {
+ m := make([]chan struct{}, n)
+ for i := range m {
+ m[i] = make(chan struct{}, 1)
+ go read(m[i])
+ }
+ b.StopTimer()
+ b.ResetTimer()
+ GC()
+
+ for i := 0; i < b.N; i++ {
+ for _, ch := range m {
+ if ch != nil {
+ ch <- struct{}{}
+ }
+ }
+ time.Sleep(10 * time.Millisecond)
+ b.StartTimer()
+ GC()
+ b.StopTimer()
+ }
+
+ for _, ch := range m {
+ close(ch)
+ }
+ time.Sleep(10 * time.Millisecond)
+}
+
+func BenchmarkGoroutineIdle(b *testing.B) {
+ quit := make(chan struct{})
+ fn := func() {
+ <-quit
+ }
+ for i := 0; i < *n; i++ {
+ go fn()
+ }
+
+ GC()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ GC()
+ }
+
+ b.StopTimer()
+ close(quit)
+ time.Sleep(10 * time.Millisecond)
+}
Debug = 0,
DebugMark = 0, // run second pass to check mark
CollectStats = 0,
- ScanStackByFrames = 1,
+ ScanStackByFrames = 0,
IgnorePreciseGC = 0,
// Four bits per word (see #defines below).
USED(guard);
runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, addframeroots, nil, false);
} else {
+ USED(lr);
USED(pc);
n = 0;
while(stk) {