Merge branch 'counter/fixed-prec' of git://github.com/smcquay/client_golang into beorn7/counter

This commit is contained in:
beorn7 2018-01-19 13:12:07 +01:00
commit c7029dc87d
2 changed files with 120 additions and 6 deletions

View File

@ -28,11 +28,25 @@ func TestCounterAdd(t *testing.T) {
ConstLabels: Labels{"a": "1", "b": "2"}, ConstLabels: Labels{"a": "1", "b": "2"},
}).(*counter) }).(*counter)
counter.Inc() counter.Inc()
if expected, got := 1., math.Float64frombits(counter.valBits); expected != got { if expected, got := 0.0, math.Float64frombits(counter.valBits); expected != got {
t.Errorf("Expected %f, got %f.", expected, got)
}
if expected, got := int64(1), counter.valInt; expected != got {
t.Errorf("Expected %f, got %f.", expected, got) t.Errorf("Expected %f, got %f.", expected, got)
} }
counter.Add(42) counter.Add(42)
if expected, got := 43., math.Float64frombits(counter.valBits); expected != got { if expected, got := 0.0, math.Float64frombits(counter.valBits); expected != got {
t.Errorf("Expected %f, got %f.", expected, got)
}
if expected, got := int64(43), counter.valInt; expected != got {
t.Errorf("Expected %f, got %f.", expected, got)
}
counter.Add(24.42)
if expected, got := 24.42, math.Float64frombits(counter.valBits); expected != got {
t.Errorf("Expected %f, got %f.", expected, got)
}
if expected, got := int64(43), counter.valInt; expected != got {
t.Errorf("Expected %f, got %f.", expected, got) t.Errorf("Expected %f, got %f.", expected, got)
} }
@ -43,7 +57,7 @@ func TestCounterAdd(t *testing.T) {
m := &dto.Metric{} m := &dto.Metric{}
counter.Write(m) counter.Write(m)
if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > counter:<value:43 > `, m.String(); expected != got { if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > counter:<value:67.42 > `, m.String(); expected != got {
t.Errorf("expected %q, got %q", expected, got) t.Errorf("expected %q, got %q", expected, got)
} }
} }
@ -112,3 +126,87 @@ func expectPanic(t *testing.T, op func(), errorMsg string) {
op() op()
} }
func TestCounterAddInf(t *testing.T) {
counter := NewCounter(CounterOpts{
Name: "test",
Help: "test help",
}).(*counter)
counter.Inc()
if expected, got := 0.0, math.Float64frombits(counter.valBits); expected != got {
t.Errorf("Expected %f, got %f.", expected, got)
}
if expected, got := int64(1), counter.valInt; expected != got {
t.Errorf("Expected %f, got %f.", expected, got)
}
counter.Add(math.Inf(1))
if expected, got := math.Inf(1), math.Float64frombits(counter.valBits); expected != got {
t.Errorf("valBits expected %f, got %f.", expected, got)
}
if expected, got := int64(1), counter.valInt; expected != got {
t.Errorf("valInts expected %d, got %d.", expected, got)
}
counter.Inc()
if expected, got := math.Inf(1), math.Float64frombits(counter.valBits); expected != got {
t.Errorf("Expected %f, got %f.", expected, got)
}
if expected, got := int64(2), counter.valInt; expected != got {
t.Errorf("Expected %d, got %d.", expected, got)
}
m := &dto.Metric{}
counter.Write(m)
if expected, got := `counter:<value:inf > `, m.String(); expected != got {
t.Errorf("expected %q, got %q", expected, got)
}
}
func TestCounterAddLarge(t *testing.T) {
counter := NewCounter(CounterOpts{
Name: "test",
Help: "test help",
}).(*counter)
// large overflows the underlying type and should therefore be stored in valBits
large := float64(math.MaxInt64 + 1)
counter.Add(large)
if expected, got := large, math.Float64frombits(counter.valBits); expected != got {
t.Errorf("valBits expected %f, got %f.", expected, got)
}
if expected, got := int64(0), counter.valInt; expected != got {
t.Errorf("valInts expected %d, got %d.", expected, got)
}
m := &dto.Metric{}
counter.Write(m)
if expected, got := fmt.Sprintf("counter:<value:%0.15e > ", large), m.String(); expected != got {
t.Errorf("expected %q, got %q", expected, got)
}
}
func TestCounterAddSmall(t *testing.T) {
counter := NewCounter(CounterOpts{
Name: "test",
Help: "test help",
}).(*counter)
small := 0.000000000001
counter.Add(small)
if expected, got := small, math.Float64frombits(counter.valBits); expected != got {
t.Errorf("valBits expected %f, got %f.", expected, got)
}
if expected, got := int64(0), counter.valInt; expected != got {
t.Errorf("valInts expected %d, got %d.", expected, got)
}
m := &dto.Metric{}
counter.Write(m)
if expected, got := fmt.Sprintf("counter:<value:%0.0e > ", small), m.String(); expected != got {
t.Errorf("expected %q, got %q", expected, got)
}
}

View File

@ -45,6 +45,8 @@ type value struct {
// to go first in the struct to guarantee alignment for atomic // to go first in the struct to guarantee alignment for atomic
// operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
valBits uint64 valBits uint64
// valInt is used to store values that are exact integers
valInt int64
selfCollector selfCollector
@ -82,15 +84,26 @@ func (v *value) SetToCurrentTime() {
v.Set(float64(time.Now().UnixNano()) / 1e9) v.Set(float64(time.Now().UnixNano()) / 1e9)
} }
// add adjusts the underlying int64
func (v *value) add(delta int64) {
atomic.AddInt64(&v.valInt, delta)
}
func (v *value) Inc() { func (v *value) Inc() {
v.Add(1) v.add(1)
} }
func (v *value) Dec() { func (v *value) Dec() {
v.Add(-1) v.add(-1)
} }
func (v *value) Add(val float64) { func (v *value) Add(val float64) {
ival := int64(val)
if float64(ival) == val {
v.add(ival)
return
}
for { for {
oldBits := atomic.LoadUint64(&v.valBits) oldBits := atomic.LoadUint64(&v.valBits)
newBits := math.Float64bits(math.Float64frombits(oldBits) + val) newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
@ -105,7 +118,10 @@ func (v *value) Sub(val float64) {
} }
func (v *value) Write(out *dto.Metric) error { func (v *value) Write(out *dto.Metric) error {
val := math.Float64frombits(atomic.LoadUint64(&v.valBits)) fval := math.Float64frombits(atomic.LoadUint64(&v.valBits))
ival := atomic.LoadInt64(&v.valInt)
val := fval + float64(ival)
return populateMetric(v.valType, val, v.labelPairs, out) return populateMetric(v.valType, val, v.labelPairs, out)
} }