diff options
author | Matt T. Proud <matt.proud@gmail.com> | 2015-02-02 00:33:44 -0800 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2015-04-12 23:07:50 +0000 |
commit | 926616da3f3781f5f71dacac429c6957dec83a65 (patch) | |
tree | 42a35dc0c3dc7d1e20e58ea2840ee7e5ce98843a /src/expvar | |
parent | ae740a459ef40f63d3a2864b70c89e316efd0c8b (diff) | |
download | go-git-926616da3f3781f5f71dacac429c6957dec83a65.tar.gz |
expvar: swap Float sync. from mutex to atomic.
Float type from a mutex to atomic bit array in a manner akin to
Google Guava's AtomicDouble[0], including adding a benchmark for the
type (benchcmp included below) along with some expvar_test.go cruft
being fixed.
benchmark old ns/op new ns/op delta
BenchmarkFloatSet 115 9.37 -91.85%
BenchmarkFloatAdd 114 17.1 -85.00%
benchmark old allocs new allocs delta
BenchmarkFloatSet 0 0 +0.00%
BenchmarkFloatAdd 0 0 +0.00%
benchmark old bytes new bytes delta
BenchmarkFloatSet 0 0 +0.00%
BenchmarkFloatAdd 0 0 +0.00%
[0] - http://goo.gl/m4dtlI
Change-Id: I4ce6a913734ec692e3ed243f6e6f7c11da4c6036
Reviewed-on: https://go-review.googlesource.com/3687
Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/expvar')
-rw-r--r-- | src/expvar/expvar.go | 25 | ||||
-rw-r--r-- | src/expvar/expvar_test.go | 18 |
2 files changed, 26 insertions, 17 deletions
diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go index 2cb515a678..24c2d6b29a 100644 --- a/src/expvar/expvar.go +++ b/src/expvar/expvar.go @@ -26,6 +26,7 @@ import ( "encoding/json" "fmt" "log" + "math" "net/http" "os" "runtime" @@ -59,28 +60,30 @@ func (v *Int) Set(value int64) { // 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/src/expvar/expvar_test.go b/src/expvar/expvar_test.go index 11e6497b96..8bc633e4a9 100644 --- a/src/expvar/expvar_test.go +++ b/src/expvar/expvar_test.go @@ -7,11 +7,13 @@ package expvar import ( "bytes" "encoding/json" + "math" "net" "net/http/httptest" "runtime" "strconv" "sync" + "sync/atomic" "testing" ) @@ -70,6 +72,10 @@ func BenchmarkIntSet(b *testing.B) { }) } +func (v *Float) val() float64 { + return math.Float64frombits(atomic.LoadUint64(&v.f)) +} + func TestFloat(t *testing.T) { RemoveAll() reqs := NewFloat("requests-float") @@ -82,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" { @@ -91,8 +97,8 @@ 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) } } @@ -157,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}', |