Improve Gauge and Counter performance.

This is accomplished by using the functions from the atomic packages
instead of a mutex.

benchmark                                      old ns/op     new ns/op     delta
BenchmarkGaugeNoLabels-2                       118           9.40          -92.03%
BenchmarkGaugeNoLabels                         117           9.38          -91.98%
BenchmarkGaugeNoLabels-4                       117           9.40          -91.97%
BenchmarkCounterNoLabels-2                     137           16.8          -87.74%
BenchmarkCounterNoLabels                       136           16.8          -87.65%
BenchmarkCounterNoLabels-4                     136           16.8          -87.65%
BenchmarkGaugeWithLabelValues-4                400           279           -30.25%
BenchmarkGaugeWithLabelValues-2                398           279           -29.90%
BenchmarkGaugeWithLabelValues                  400           283           -29.25%
BenchmarkCounterWithLabelValues-4              397           286           -27.96%
BenchmarkCounterWithLabelValues-2              396           286           -27.78%
BenchmarkCounterWithLabelValues                394           285           -27.66%
BenchmarkCounterWithPreparedMappedLabels       587           454           -22.66%
BenchmarkCounterWithPreparedMappedLabels-2     581           456           -21.51%
BenchmarkCounterWithPreparedMappedLabels-4     654           539           -17.58%
BenchmarkCounterWithMappedLabels-2             1441          1218          -15.48%
BenchmarkCounterWithMappedLabels               1099          963           -12.37%
BenchmarkCounterWithMappedLabels-4             1636          1501          -8.25%
This commit is contained in:
beorn7 2015-02-02 18:02:40 +01:00
parent 88b6ea5852
commit 4f73a8b017
3 changed files with 18 additions and 20 deletions

View File

@ -14,6 +14,7 @@
package prometheus package prometheus
import ( import (
"math"
"testing" "testing"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
@ -26,11 +27,11 @@ 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., counter.val; expected != got { if expected, got := 1., math.Float64frombits(counter.valBits); 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., counter.val; expected != got { if expected, got := 43., math.Float64frombits(counter.valBits); expected != got {
t.Errorf("Expected %f, got %f.", expected, got) t.Errorf("Expected %f, got %f.", expected, got)
} }

View File

@ -82,7 +82,7 @@ func TestGaugeConcurrency(t *testing.T) {
} }
start.Done() start.Done()
if expected, got := <-result, gge.(*value).val; math.Abs(expected-got) > 0.000001 { if expected, got := <-result, math.Float64frombits(gge.(*value).valBits); math.Abs(expected-got) > 0.000001 {
t.Fatalf("expected approx. %f, got %f", expected, got) t.Fatalf("expected approx. %f, got %f", expected, got)
return false return false
} }
@ -146,7 +146,7 @@ func TestGaugeVecConcurrency(t *testing.T) {
start.Done() start.Done()
for i := range sStreams { for i := range sStreams {
if expected, got := <-results[i], gge.WithLabelValues(string('A'+i)).(*value).val; math.Abs(expected-got) > 0.000001 { if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*value).valBits); math.Abs(expected-got) > 0.000001 {
t.Fatalf("expected approx. %f, got %f", expected, got) t.Fatalf("expected approx. %f, got %f", expected, got)
return false return false
} }

View File

@ -16,8 +16,9 @@ package prometheus
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"sort" "sort"
"sync" "sync/atomic"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
@ -44,10 +45,9 @@ var errInconsistentCardinality = errors.New("inconsistent label cardinality")
type value struct { type value struct {
SelfCollector SelfCollector
mtx sync.RWMutex
desc *Desc desc *Desc
valType ValueType valType ValueType
val float64 valBits uint64 // These are the bits of the represented float64 value.
labelPairs []*dto.LabelPair labelPairs []*dto.LabelPair
} }
@ -61,7 +61,7 @@ func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...strin
result := &value{ result := &value{
desc: desc, desc: desc,
valType: valueType, valType: valueType,
val: val, valBits: math.Float64bits(val),
labelPairs: makeLabelPairs(desc, labelValues), labelPairs: makeLabelPairs(desc, labelValues),
} }
result.Init(result) result.Init(result)
@ -73,10 +73,7 @@ func (v *value) Desc() *Desc {
} }
func (v *value) Set(val float64) { func (v *value) Set(val float64) {
v.mtx.Lock() atomic.StoreUint64(&v.valBits, math.Float64bits(val))
defer v.mtx.Unlock()
v.val = val
} }
func (v *value) Inc() { func (v *value) Inc() {
@ -88,10 +85,13 @@ func (v *value) Dec() {
} }
func (v *value) Add(val float64) { func (v *value) Add(val float64) {
v.mtx.Lock() for {
defer v.mtx.Unlock() oldBits := atomic.LoadUint64(&v.valBits)
newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
v.val += val if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) {
return
}
}
} }
func (v *value) Sub(val float64) { func (v *value) Sub(val float64) {
@ -99,10 +99,7 @@ func (v *value) Sub(val float64) {
} }
func (v *value) Write(out *dto.Metric) error { func (v *value) Write(out *dto.Metric) error {
v.mtx.RLock() val := math.Float64frombits(atomic.LoadUint64(&v.valBits))
val := v.val
v.mtx.RUnlock()
return populateMetric(v.valType, val, v.labelPairs, out) return populateMetric(v.valType, val, v.labelPairs, out)
} }