]> Cypherpunks repositories - gostls13.git/commitdiff
expvar: swap Float sync. from mutex to atomic.
authorMatt T. Proud <matt.proud@gmail.com>
Mon, 2 Feb 2015 08:33:44 +0000 (00:33 -0800)
committerRob Pike <r@golang.org>
Sun, 12 Apr 2015 23:07:50 +0000 (23:07 +0000)
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>
src/expvar/expvar.go
src/expvar/expvar_test.go

index 2cb515a6784567621a922443f7dcda5bd3cf3b69..24c2d6b29ab4221928104eb052ff693bb223f733 100644 (file)
@@ -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.
index 11e6497b96b0fbb38f16c9ebea048aea21ae83b2..8bc633e4a9767acd61bd28e6cd6578b3046eca70 100644 (file)
@@ -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}',