diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-10-31 00:59:47 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-10-31 00:59:47 +0000 |
commit | 4a3da3a8a45d5496118798146de1fa4e5798ed5a (patch) | |
tree | 13beeaed3698c61903fe93fb1ce70bd9b18d4e7f /libgo/go/expvar | |
parent | cd529f4d86a17a3e8959f2cb5ac7132a841ab6f1 (diff) | |
download | gcc-4a3da3a8a45d5496118798146de1fa4e5798ed5a.tar.gz |
runtime: Remove now unnecessary pad field from ParFor.
It is not needed due to the removal of the ctx field.
Reviewed-on: https://go-review.googlesource.com/16525
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@229616 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/expvar')
-rw-r--r-- | libgo/go/expvar/expvar.go | 41 | ||||
-rw-r--r-- | libgo/go/expvar/expvar_test.go | 235 |
2 files changed, 248 insertions, 28 deletions
diff --git a/libgo/go/expvar/expvar.go b/libgo/go/expvar/expvar.go index 9b6dab487cb..24c2d6b29ab 100644 --- a/libgo/go/expvar/expvar.go +++ b/libgo/go/expvar/expvar.go @@ -26,12 +26,14 @@ import ( "encoding/json" "fmt" "log" + "math" "net/http" "os" "runtime" "sort" "strconv" "sync" + "sync/atomic" ) // Var is an abstract type for all exported variables. @@ -41,52 +43,47 @@ type Var interface { // Int is a 64-bit integer variable that satisfies the Var interface. type Int struct { - mu sync.RWMutex - i int64 + i int64 } func (v *Int) String() string { - v.mu.RLock() - defer v.mu.RUnlock() - return strconv.FormatInt(v.i, 10) + return strconv.FormatInt(atomic.LoadInt64(&v.i), 10) } func (v *Int) Add(delta int64) { - v.mu.Lock() - defer v.mu.Unlock() - v.i += delta + atomic.AddInt64(&v.i, delta) } func (v *Int) Set(value int64) { - v.mu.Lock() - defer v.mu.Unlock() - v.i = value + atomic.StoreInt64(&v.i, value) } // Float is a 64-bit float variable that satisfies the Var interface. type Float struct { - mu sync.RWMutex - f float64 + f uint64 } func (v *Float) String() string { - v.mu.RLock() - defer v.mu.RUnlock() - return strconv.FormatFloat(v.f, 'g', -1, 64) + return strconv.FormatFloat( + math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64) } // Add adds delta to v. func (v *Float) Add(delta float64) { - v.mu.Lock() - defer v.mu.Unlock() - v.f += delta + for { + cur := atomic.LoadUint64(&v.f) + curVal := math.Float64frombits(cur) + nxtVal := curVal + delta + nxt := math.Float64bits(nxtVal) + if atomic.CompareAndSwapUint64(&v.f, cur, nxt) { + return + } + } } // Set sets v to value. func (v *Float) Set(value float64) { - v.mu.Lock() - defer v.mu.Unlock() - v.f = value + atomic.StoreUint64(&v.f, math.Float64bits(value)) } // Map is a string-to-Var map variable that satisfies the Var interface. diff --git a/libgo/go/expvar/expvar_test.go b/libgo/go/expvar/expvar_test.go index 765e3b757e9..8bc633e4a97 100644 --- a/libgo/go/expvar/expvar_test.go +++ b/libgo/go/expvar/expvar_test.go @@ -7,8 +7,13 @@ package expvar import ( "bytes" "encoding/json" + "math" + "net" "net/http/httptest" + "runtime" "strconv" + "sync" + "sync/atomic" "testing" ) @@ -47,6 +52,30 @@ func TestInt(t *testing.T) { } } +func BenchmarkIntAdd(b *testing.B) { + var v Int + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + v.Add(1) + } + }) +} + +func BenchmarkIntSet(b *testing.B) { + var v Int + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + v.Set(1) + } + }) +} + +func (v *Float) val() float64 { + return math.Float64frombits(atomic.LoadUint64(&v.f)) +} + func TestFloat(t *testing.T) { RemoveAll() reqs := NewFloat("requests-float") @@ -59,8 +88,8 @@ func TestFloat(t *testing.T) { reqs.Add(1.5) reqs.Add(1.25) - if reqs.f != 2.75 { - t.Errorf("reqs.f = %v, want 2.75", reqs.f) + if v := reqs.val(); v != 2.75 { + t.Errorf("reqs.val() = %v, want 2.75", v) } if s := reqs.String(); s != "2.75" { @@ -68,11 +97,31 @@ func TestFloat(t *testing.T) { } reqs.Add(-2) - if reqs.f != 0.75 { - t.Errorf("reqs.f = %v, want 0.75", reqs.f) + if v := reqs.val(); v != 0.75 { + t.Errorf("reqs.val() = %v, want 0.75", v) } } +func BenchmarkFloatAdd(b *testing.B) { + var f Float + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Add(1.0) + } + }) +} + +func BenchmarkFloatSet(b *testing.B) { + var f Float + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Set(1.0) + } + }) +} + func TestString(t *testing.T) { RemoveAll() name := NewString("my-name") @@ -90,6 +139,16 @@ func TestString(t *testing.T) { } } +func BenchmarkStringSet(b *testing.B) { + var s String + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + s.Set("red") + } + }) +} + func TestMapCounter(t *testing.T) { RemoveAll() colors := NewMap("bike-shed-colors") @@ -104,8 +163,8 @@ func TestMapCounter(t *testing.T) { if x := colors.m["blue"].(*Int).i; x != 4 { t.Errorf("colors.m[\"blue\"] = %v, want 4", x) } - if x := colors.m[`green "midori"`].(*Float).f; x != 4.125 { - t.Errorf("colors.m[`green \"midori\"] = %v, want 3.14", x) + if x := colors.m[`green "midori"`].(*Float).val(); x != 4.125 { + t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x) } // colors.String() should be '{"red":3, "blue":4}', @@ -130,6 +189,38 @@ func TestMapCounter(t *testing.T) { } } +func BenchmarkMapSet(b *testing.B) { + m := new(Map).Init() + + v := new(Int) + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + m.Set("red", v) + } + }) +} + +func BenchmarkMapAddSame(b *testing.B) { + for i := 0; i < b.N; i++ { + m := new(Map).Init() + m.Add("red", 1) + m.Add("red", 1) + m.Add("red", 1) + m.Add("red", 1) + } +} + +func BenchmarkMapAddDifferent(b *testing.B) { + for i := 0; i < b.N; i++ { + m := new(Map).Init() + m.Add("red", 1) + m.Add("blue", 1) + m.Add("green", 1) + m.Add("yellow", 1) + } +} + func TestFunc(t *testing.T) { RemoveAll() var x interface{} = []string{"a", "b"} @@ -165,3 +256,135 @@ func TestHandler(t *testing.T) { t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want) } } + +func BenchmarkRealworldExpvarUsage(b *testing.B) { + var ( + bytesSent Int + bytesRead Int + ) + + // The benchmark creates GOMAXPROCS client/server pairs. + // Each pair creates 4 goroutines: client reader/writer and server reader/writer. + // The benchmark stresses concurrent reading and writing to the same connection. + // Such pattern is used in net/http and net/rpc. + + b.StopTimer() + + P := runtime.GOMAXPROCS(0) + N := b.N / P + W := 1000 + + // Setup P client/server connections. + clients := make([]net.Conn, P) + servers := make([]net.Conn, P) + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + b.Fatalf("Listen failed: %v", err) + } + defer ln.Close() + done := make(chan bool) + go func() { + for p := 0; p < P; p++ { + s, err := ln.Accept() + if err != nil { + b.Errorf("Accept failed: %v", err) + return + } + servers[p] = s + } + done <- true + }() + for p := 0; p < P; p++ { + c, err := net.Dial("tcp", ln.Addr().String()) + if err != nil { + b.Fatalf("Dial failed: %v", err) + } + clients[p] = c + } + <-done + + b.StartTimer() + + var wg sync.WaitGroup + wg.Add(4 * P) + for p := 0; p < P; p++ { + // Client writer. + go func(c net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + v := byte(i) + for w := 0; w < W; w++ { + v *= v + } + buf[0] = v + n, err := c.Write(buf[:]) + if err != nil { + b.Errorf("Write failed: %v", err) + return + } + + bytesSent.Add(int64(n)) + } + }(clients[p]) + + // Pipe between server reader and server writer. + pipe := make(chan byte, 128) + + // Server reader. + go func(s net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + n, err := s.Read(buf[:]) + + if err != nil { + b.Errorf("Read failed: %v", err) + return + } + + bytesRead.Add(int64(n)) + pipe <- buf[0] + } + }(servers[p]) + + // Server writer. + go func(s net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + v := <-pipe + for w := 0; w < W; w++ { + v *= v + } + buf[0] = v + n, err := s.Write(buf[:]) + if err != nil { + b.Errorf("Write failed: %v", err) + return + } + + bytesSent.Add(int64(n)) + } + s.Close() + }(servers[p]) + + // Client reader. + go func(c net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + n, err := c.Read(buf[:]) + + if err != nil { + b.Errorf("Read failed: %v", err) + return + } + + bytesRead.Add(int64(n)) + } + c.Close() + }(clients[p]) + } + wg.Wait() +} |