Commit 926616da authored by Matt T. Proud's avatar Matt T. Proud Committed by Rob Pike

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/3687Reviewed-by: default avatarRob Pike <r@golang.org>
parent ae740a45
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
"math"
"net/http" "net/http"
"os" "os"
"runtime" "runtime"
...@@ -59,28 +60,30 @@ func (v *Int) Set(value int64) { ...@@ -59,28 +60,30 @@ func (v *Int) Set(value int64) {
// Float is a 64-bit float variable that satisfies the Var interface. // Float is a 64-bit float variable that satisfies the Var interface.
type Float struct { type Float struct {
mu sync.RWMutex f uint64
f float64
} }
func (v *Float) String() string { func (v *Float) String() string {
v.mu.RLock() return strconv.FormatFloat(
defer v.mu.RUnlock() math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64)
return strconv.FormatFloat(v.f, 'g', -1, 64)
} }
// Add adds delta to v. // Add adds delta to v.
func (v *Float) Add(delta float64) { func (v *Float) Add(delta float64) {
v.mu.Lock() for {
defer v.mu.Unlock() cur := atomic.LoadUint64(&v.f)
v.f += delta curVal := math.Float64frombits(cur)
nxtVal := curVal + delta
nxt := math.Float64bits(nxtVal)
if atomic.CompareAndSwapUint64(&v.f, cur, nxt) {
return
}
}
} }
// Set sets v to value. // Set sets v to value.
func (v *Float) Set(value float64) { func (v *Float) Set(value float64) {
v.mu.Lock() atomic.StoreUint64(&v.f, math.Float64bits(value))
defer v.mu.Unlock()
v.f = value
} }
// Map is a string-to-Var map variable that satisfies the Var interface. // Map is a string-to-Var map variable that satisfies the Var interface.
......
...@@ -7,11 +7,13 @@ package expvar ...@@ -7,11 +7,13 @@ package expvar
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"math"
"net" "net"
"net/http/httptest" "net/http/httptest"
"runtime" "runtime"
"strconv" "strconv"
"sync" "sync"
"sync/atomic"
"testing" "testing"
) )
...@@ -70,6 +72,10 @@ func BenchmarkIntSet(b *testing.B) { ...@@ -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) { func TestFloat(t *testing.T) {
RemoveAll() RemoveAll()
reqs := NewFloat("requests-float") reqs := NewFloat("requests-float")
...@@ -82,8 +88,8 @@ func TestFloat(t *testing.T) { ...@@ -82,8 +88,8 @@ func TestFloat(t *testing.T) {
reqs.Add(1.5) reqs.Add(1.5)
reqs.Add(1.25) reqs.Add(1.25)
if reqs.f != 2.75 { if v := reqs.val(); v != 2.75 {
t.Errorf("reqs.f = %v, want 2.75", reqs.f) t.Errorf("reqs.val() = %v, want 2.75", v)
} }
if s := reqs.String(); s != "2.75" { if s := reqs.String(); s != "2.75" {
...@@ -91,8 +97,8 @@ func TestFloat(t *testing.T) { ...@@ -91,8 +97,8 @@ func TestFloat(t *testing.T) {
} }
reqs.Add(-2) reqs.Add(-2)
if reqs.f != 0.75 { if v := reqs.val(); v != 0.75 {
t.Errorf("reqs.f = %v, want 0.75", reqs.f) t.Errorf("reqs.val() = %v, want 0.75", v)
} }
} }
...@@ -157,8 +163,8 @@ func TestMapCounter(t *testing.T) { ...@@ -157,8 +163,8 @@ func TestMapCounter(t *testing.T) {
if x := colors.m["blue"].(*Int).i; x != 4 { if x := colors.m["blue"].(*Int).i; x != 4 {
t.Errorf("colors.m[\"blue\"] = %v, want 4", x) t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
} }
if x := colors.m[`green "midori"`].(*Float).f; x != 4.125 { if x := colors.m[`green "midori"`].(*Float).val(); x != 4.125 {
t.Errorf("colors.m[`green \"midori\"] = %v, want 3.14", x) t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x)
} }
// colors.String() should be '{"red":3, "blue":4}', // colors.String() should be '{"red":3, "blue":4}',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment