From 4f73a8b017436a11b94e63d58e9dec2baa42c627 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Mon, 2 Feb 2015 18:02:40 +0100 Subject: [PATCH] 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% --- prometheus/counter_test.go | 5 +++-- prometheus/gauge_test.go | 4 ++-- prometheus/value.go | 29 +++++++++++++---------------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/prometheus/counter_test.go b/prometheus/counter_test.go index 3d8bc37..67391a2 100644 --- a/prometheus/counter_test.go +++ b/prometheus/counter_test.go @@ -14,6 +14,7 @@ package prometheus import ( + "math" "testing" dto "github.com/prometheus/client_model/go" @@ -26,11 +27,11 @@ func TestCounterAdd(t *testing.T) { ConstLabels: Labels{"a": "1", "b": "2"}, }).(*counter) 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) } 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) } diff --git a/prometheus/gauge_test.go b/prometheus/gauge_test.go index 6ff1d33..48cab46 100644 --- a/prometheus/gauge_test.go +++ b/prometheus/gauge_test.go @@ -82,7 +82,7 @@ func TestGaugeConcurrency(t *testing.T) { } 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) return false } @@ -146,7 +146,7 @@ func TestGaugeVecConcurrency(t *testing.T) { start.Done() 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) return false } diff --git a/prometheus/value.go b/prometheus/value.go index be74b7f..2db1d11 100644 --- a/prometheus/value.go +++ b/prometheus/value.go @@ -16,8 +16,9 @@ package prometheus import ( "errors" "fmt" + "math" "sort" - "sync" + "sync/atomic" dto "github.com/prometheus/client_model/go" @@ -44,10 +45,9 @@ var errInconsistentCardinality = errors.New("inconsistent label cardinality") type value struct { SelfCollector - mtx sync.RWMutex desc *Desc valType ValueType - val float64 + valBits uint64 // These are the bits of the represented float64 value. labelPairs []*dto.LabelPair } @@ -61,7 +61,7 @@ func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...strin result := &value{ desc: desc, valType: valueType, - val: val, + valBits: math.Float64bits(val), labelPairs: makeLabelPairs(desc, labelValues), } result.Init(result) @@ -73,10 +73,7 @@ func (v *value) Desc() *Desc { } func (v *value) Set(val float64) { - v.mtx.Lock() - defer v.mtx.Unlock() - - v.val = val + atomic.StoreUint64(&v.valBits, math.Float64bits(val)) } func (v *value) Inc() { @@ -88,10 +85,13 @@ func (v *value) Dec() { } func (v *value) Add(val float64) { - v.mtx.Lock() - defer v.mtx.Unlock() - - v.val += val + for { + oldBits := atomic.LoadUint64(&v.valBits) + newBits := math.Float64bits(math.Float64frombits(oldBits) + val) + if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) { + return + } + } } func (v *value) Sub(val float64) { @@ -99,10 +99,7 @@ func (v *value) Sub(val float64) { } func (v *value) Write(out *dto.Metric) error { - v.mtx.RLock() - val := v.val - v.mtx.RUnlock() - + val := math.Float64frombits(atomic.LoadUint64(&v.valBits)) return populateMetric(v.valType, val, v.labelPairs, out) }