client_golang/prometheus/gauge.go

140 lines
2.8 KiB
Go
Raw Normal View History

// Copyright (c) 2013, Prometheus Team
// All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2012-05-20 01:59:25 +04:00
package prometheus
2012-05-20 01:59:25 +04:00
import (
"encoding/json"
2012-05-20 01:59:25 +04:00
"fmt"
"sync"
"code.google.com/p/goprotobuf/proto"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/client_golang/model"
2012-05-20 01:59:25 +04:00
)
// A gauge metric merely provides an instantaneous representation of a scalar
// value or an accumulation. For instance, if one wants to expose the current
// temperature or the hitherto bandwidth used, this would be the metric for such
// circumstances.
type Gauge interface {
Metric
Set(labels map[string]string, value float64) float64
2012-05-20 01:59:25 +04:00
}
type gaugeVector struct {
Labels map[string]string `json:"labels"`
Value float64 `json:"value"`
}
func NewGauge() Gauge {
return &gauge{
values: map[uint64]*gaugeVector{},
}
}
type gauge struct {
mutex sync.RWMutex
values map[uint64]*gaugeVector
}
func (metric *gauge) String() string {
formatString := "[Gauge %s]"
2012-05-20 01:59:25 +04:00
metric.mutex.RLock()
defer metric.mutex.RUnlock()
return fmt.Sprintf(formatString, metric.values)
2012-05-20 01:59:25 +04:00
}
func (metric *gauge) Set(labels map[string]string, value float64) float64 {
if labels == nil {
Optimize fingerprinting and metric locks. These are all simple changes we should have caught a long time ago: 1. The hashing mechanism for fingerprint label sets should have not allocated new objects for the actual hashing---at least not egregiously. This simplifies the hash writing by just byte- dumping the string stream into the hasher. 2. The hashing mechanism within the scope of a metric does not care about the value of the label keys themselves but only of the label values. The keys can be dropped from the calculation. 3. The locking mechanism for the metrics should not block on hash computation but rather solely on the actual mutation or critical section reads. 4. For scalar metrics (i.e., ones with niladic label signatures), we should rely on a preallocated map versus requesting a new one ad hoc. This is tested with Go 1.1, so the results may yield other values for us elsewhere: BEFORE BenchmarkLabelValuesToSignatureScalar 500000000 3.97 ns/op 0 B/op 0 allocs/op BenchmarkLabelValuesToSignatureSingle 5000000 714 ns/op 74 B/op 4 allocs/op BenchmarkLabelValuesToSignatureDouble 1000000 1153 ns/op 107 B/op 5 allocs/op BenchmarkLabelValuesToSignatureTriple 1000000 1588 ns/op 138 B/op 6 allocs/op BenchmarkLabelToSignatureScalar 500000000 3.91 ns/op 0 B/op 0 allocs/op BenchmarkLabelToSignatureSingle 2000000 874 ns/op 92 B/op 5 allocs/op BenchmarkLabelToSignatureDouble 1000000 1528 ns/op 139 B/op 7 allocs/op BenchmarkLabelToSignatureTriple 1000000 2172 ns/op 186 B/op 9 allocs/op AFTER BenchmarkLabelValuesToSignatureScalar 500000000 4.36 ns/op 0 B/op 0 allocs/op BenchmarkLabelValuesToSignatureSingle 5000000 378 ns/op 89 B/op 4 allocs/op BenchmarkLabelValuesToSignatureDouble 5000000 574 ns/op 142 B/op 5 allocs/op BenchmarkLabelValuesToSignatureTriple 5000000 758 ns/op 186 B/op 6 allocs/op BenchmarkLabelToSignatureScalar 500000000 4.06 ns/op 0 B/op 0 allocs/op BenchmarkLabelToSignatureSingle 5000000 472 ns/op 106 B/op 5 allocs/op BenchmarkLabelToSignatureDouble 2000000 746 ns/op 174 B/op 7 allocs/op BenchmarkLabelToSignatureTriple 1000000 1061 ns/op 235 B/op 9 allocs/op In effect, a single metric mutation operation's lookup overhead will move from Before::iBenchmarkLabelToSignature to After::BenchmarkLabelValuesToSignature. This MINIMALLY reduces 1/2 the overhead. I would be hesitant in reading the memory allocation statistics, for this was run with the GC still on and thusly inaccurate per Go benchmarking documentation. Before::BenchmarkLabelValuesToSignature never existed, so it is not of any intrinsic value in itself. That said, the cases that still rely on LabelToSignature experience consistently a 1/2 drop in time. Change-Id: Ifc9e69f718af65a59f5be8117473518233258159
2014-04-14 20:45:16 +04:00
labels = blankLabelsSingleton
}
signature := model.LabelValuesToSignature(labels)
Optimize fingerprinting and metric locks. These are all simple changes we should have caught a long time ago: 1. The hashing mechanism for fingerprint label sets should have not allocated new objects for the actual hashing---at least not egregiously. This simplifies the hash writing by just byte- dumping the string stream into the hasher. 2. The hashing mechanism within the scope of a metric does not care about the value of the label keys themselves but only of the label values. The keys can be dropped from the calculation. 3. The locking mechanism for the metrics should not block on hash computation but rather solely on the actual mutation or critical section reads. 4. For scalar metrics (i.e., ones with niladic label signatures), we should rely on a preallocated map versus requesting a new one ad hoc. This is tested with Go 1.1, so the results may yield other values for us elsewhere: BEFORE BenchmarkLabelValuesToSignatureScalar 500000000 3.97 ns/op 0 B/op 0 allocs/op BenchmarkLabelValuesToSignatureSingle 5000000 714 ns/op 74 B/op 4 allocs/op BenchmarkLabelValuesToSignatureDouble 1000000 1153 ns/op 107 B/op 5 allocs/op BenchmarkLabelValuesToSignatureTriple 1000000 1588 ns/op 138 B/op 6 allocs/op BenchmarkLabelToSignatureScalar 500000000 3.91 ns/op 0 B/op 0 allocs/op BenchmarkLabelToSignatureSingle 2000000 874 ns/op 92 B/op 5 allocs/op BenchmarkLabelToSignatureDouble 1000000 1528 ns/op 139 B/op 7 allocs/op BenchmarkLabelToSignatureTriple 1000000 2172 ns/op 186 B/op 9 allocs/op AFTER BenchmarkLabelValuesToSignatureScalar 500000000 4.36 ns/op 0 B/op 0 allocs/op BenchmarkLabelValuesToSignatureSingle 5000000 378 ns/op 89 B/op 4 allocs/op BenchmarkLabelValuesToSignatureDouble 5000000 574 ns/op 142 B/op 5 allocs/op BenchmarkLabelValuesToSignatureTriple 5000000 758 ns/op 186 B/op 6 allocs/op BenchmarkLabelToSignatureScalar 500000000 4.06 ns/op 0 B/op 0 allocs/op BenchmarkLabelToSignatureSingle 5000000 472 ns/op 106 B/op 5 allocs/op BenchmarkLabelToSignatureDouble 2000000 746 ns/op 174 B/op 7 allocs/op BenchmarkLabelToSignatureTriple 1000000 1061 ns/op 235 B/op 9 allocs/op In effect, a single metric mutation operation's lookup overhead will move from Before::iBenchmarkLabelToSignature to After::BenchmarkLabelValuesToSignature. This MINIMALLY reduces 1/2 the overhead. I would be hesitant in reading the memory allocation statistics, for this was run with the GC still on and thusly inaccurate per Go benchmarking documentation. Before::BenchmarkLabelValuesToSignature never existed, so it is not of any intrinsic value in itself. That said, the cases that still rely on LabelToSignature experience consistently a 1/2 drop in time. Change-Id: Ifc9e69f718af65a59f5be8117473518233258159
2014-04-14 20:45:16 +04:00
metric.mutex.Lock()
defer metric.mutex.Unlock()
2012-05-20 01:59:25 +04:00
if original, ok := metric.values[signature]; ok {
original.Value = value
} else {
metric.values[signature] = &gaugeVector{
Labels: labels,
Value: value,
}
}
return value
2012-05-20 01:59:25 +04:00
}
func (metric *gauge) Reset(labels map[string]string) {
signature := model.LabelValuesToSignature(labels)
Optimize fingerprinting and metric locks. These are all simple changes we should have caught a long time ago: 1. The hashing mechanism for fingerprint label sets should have not allocated new objects for the actual hashing---at least not egregiously. This simplifies the hash writing by just byte- dumping the string stream into the hasher. 2. The hashing mechanism within the scope of a metric does not care about the value of the label keys themselves but only of the label values. The keys can be dropped from the calculation. 3. The locking mechanism for the metrics should not block on hash computation but rather solely on the actual mutation or critical section reads. 4. For scalar metrics (i.e., ones with niladic label signatures), we should rely on a preallocated map versus requesting a new one ad hoc. This is tested with Go 1.1, so the results may yield other values for us elsewhere: BEFORE BenchmarkLabelValuesToSignatureScalar 500000000 3.97 ns/op 0 B/op 0 allocs/op BenchmarkLabelValuesToSignatureSingle 5000000 714 ns/op 74 B/op 4 allocs/op BenchmarkLabelValuesToSignatureDouble 1000000 1153 ns/op 107 B/op 5 allocs/op BenchmarkLabelValuesToSignatureTriple 1000000 1588 ns/op 138 B/op 6 allocs/op BenchmarkLabelToSignatureScalar 500000000 3.91 ns/op 0 B/op 0 allocs/op BenchmarkLabelToSignatureSingle 2000000 874 ns/op 92 B/op 5 allocs/op BenchmarkLabelToSignatureDouble 1000000 1528 ns/op 139 B/op 7 allocs/op BenchmarkLabelToSignatureTriple 1000000 2172 ns/op 186 B/op 9 allocs/op AFTER BenchmarkLabelValuesToSignatureScalar 500000000 4.36 ns/op 0 B/op 0 allocs/op BenchmarkLabelValuesToSignatureSingle 5000000 378 ns/op 89 B/op 4 allocs/op BenchmarkLabelValuesToSignatureDouble 5000000 574 ns/op 142 B/op 5 allocs/op BenchmarkLabelValuesToSignatureTriple 5000000 758 ns/op 186 B/op 6 allocs/op BenchmarkLabelToSignatureScalar 500000000 4.06 ns/op 0 B/op 0 allocs/op BenchmarkLabelToSignatureSingle 5000000 472 ns/op 106 B/op 5 allocs/op BenchmarkLabelToSignatureDouble 2000000 746 ns/op 174 B/op 7 allocs/op BenchmarkLabelToSignatureTriple 1000000 1061 ns/op 235 B/op 9 allocs/op In effect, a single metric mutation operation's lookup overhead will move from Before::iBenchmarkLabelToSignature to After::BenchmarkLabelValuesToSignature. This MINIMALLY reduces 1/2 the overhead. I would be hesitant in reading the memory allocation statistics, for this was run with the GC still on and thusly inaccurate per Go benchmarking documentation. Before::BenchmarkLabelValuesToSignature never existed, so it is not of any intrinsic value in itself. That said, the cases that still rely on LabelToSignature experience consistently a 1/2 drop in time. Change-Id: Ifc9e69f718af65a59f5be8117473518233258159
2014-04-14 20:45:16 +04:00
metric.mutex.Lock()
defer metric.mutex.Unlock()
delete(metric.values, signature)
}
func (metric *gauge) ResetAll() {
metric.mutex.Lock()
defer metric.mutex.Unlock()
2012-05-20 01:59:25 +04:00
for key, value := range metric.values {
for label := range value.Labels {
delete(value.Labels, label)
}
delete(metric.values, key)
}
2012-05-20 01:59:25 +04:00
}
func (metric *gauge) MarshalJSON() ([]byte, error) {
2012-05-20 01:59:25 +04:00
metric.mutex.RLock()
defer metric.mutex.RUnlock()
values := make([]*gaugeVector, 0, len(metric.values))
for _, value := range metric.values {
values = append(values, value)
}
return json.Marshal(map[string]interface{}{
typeKey: gaugeTypeValue,
valueKey: values,
})
2012-05-20 01:59:25 +04:00
}
func (metric *gauge) dumpChildren(f *dto.MetricFamily) {
metric.mutex.RLock()
defer metric.mutex.RUnlock()
f.Type = dto.MetricType_GAUGE.Enum()
for _, child := range metric.values {
c := &dto.Gauge{
Value: proto.Float64(child.Value),
}
m := &dto.Metric{
Gauge: c,
}
for name, value := range child.Labels {
p := &dto.LabelPair{
Name: proto.String(name),
Value: proto.String(value),
}
m.Label = append(m.Label, p)
}
f.Metric = append(f.Metric, m)
}
}