summaryrefslogtreecommitdiff
path: root/libgo/go/expvar
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-31 00:59:47 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-31 00:59:47 +0000
commit4a3da3a8a45d5496118798146de1fa4e5798ed5a (patch)
tree13beeaed3698c61903fe93fb1ce70bd9b18d4e7f /libgo/go/expvar
parentcd529f4d86a17a3e8959f2cb5ac7132a841ab6f1 (diff)
downloadgcc-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.go41
-rw-r--r--libgo/go/expvar/expvar_test.go235
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()
+}