diff --git a/prometheus/counter.go b/prometheus/counter.go index a2952d1..d2a564b 100644 --- a/prometheus/counter.go +++ b/prometheus/counter.go @@ -15,7 +15,6 @@ package prometheus import ( "errors" - "hash/fnv" ) // Counter is a Metric that represents a single numerical value that only ever @@ -97,7 +96,6 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { MetricVec: MetricVec{ children: map[uint64]Metric{}, desc: desc, - hash: fnv.New64a(), newMetric: func(lvs ...string) Metric { result := &counter{value: value{ desc: desc, diff --git a/prometheus/fnv.go b/prometheus/fnv.go new file mode 100644 index 0000000..59ecedc --- /dev/null +++ b/prometheus/fnv.go @@ -0,0 +1,22 @@ +package prometheus + +// Inline and byte-free variant of hash/fnv's fnv64a. + +const ( + offset64 = 14695981039346656037 + prime64 = 1099511628211 +) + +// hashNew initializies a new fnv64a hash value. +func hashNew() uint64 { + return offset64 +} + +// hashAdd adds a string to a fnv64a hash value, returning the updated hash. +func hashAdd(h uint64, s string) uint64 { + for i := 0; i < len(s); i++ { + h ^= uint64(s[i]) + h *= prime64 + } + return h +} diff --git a/prometheus/gauge.go b/prometheus/gauge.go index ba8a402..390c074 100644 --- a/prometheus/gauge.go +++ b/prometheus/gauge.go @@ -13,8 +13,6 @@ package prometheus -import "hash/fnv" - // Gauge is a Metric that represents a single numerical value that can // arbitrarily go up and down. // @@ -77,7 +75,6 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { MetricVec: MetricVec{ children: map[uint64]Metric{}, desc: desc, - hash: fnv.New64a(), newMetric: func(lvs ...string) Metric { return newValue(desc, GaugeValue, 0, lvs...) }, diff --git a/prometheus/histogram.go b/prometheus/histogram.go index f98a41b..7a68910 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -15,7 +15,6 @@ package prometheus import ( "fmt" - "hash/fnv" "math" "sort" "sync/atomic" @@ -305,7 +304,6 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec { MetricVec: MetricVec{ children: map[uint64]Metric{}, desc: desc, - hash: fnv.New64a(), newMetric: func(lvs ...string) Metric { return newHistogram(desc, opts, lvs...) }, diff --git a/prometheus/histogram_test.go b/prometheus/histogram_test.go index 11cf66b..d1242e0 100644 --- a/prometheus/histogram_test.go +++ b/prometheus/histogram_test.go @@ -127,7 +127,7 @@ func TestHistogramConcurrency(t *testing.T) { if testing.Short() { t.Skip("Skipping test in short mode.") } - + rand.Seed(42) it := func(n uint32) bool { diff --git a/prometheus/summary.go b/prometheus/summary.go index fe81e00..eb84961 100644 --- a/prometheus/summary.go +++ b/prometheus/summary.go @@ -15,7 +15,6 @@ package prometheus import ( "fmt" - "hash/fnv" "math" "sort" "sync" @@ -408,7 +407,6 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { MetricVec: MetricVec{ children: map[uint64]Metric{}, desc: desc, - hash: fnv.New64a(), newMetric: func(lvs ...string) Metric { return newSummary(desc, opts, lvs...) }, diff --git a/prometheus/untyped.go b/prometheus/untyped.go index c65ab1c..89b86ea 100644 --- a/prometheus/untyped.go +++ b/prometheus/untyped.go @@ -13,8 +13,6 @@ package prometheus -import "hash/fnv" - // Untyped is a Metric that represents a single numerical value that can // arbitrarily go up and down. // @@ -75,7 +73,6 @@ func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec { MetricVec: MetricVec{ children: map[uint64]Metric{}, desc: desc, - hash: fnv.New64a(), newMetric: func(lvs ...string) Metric { return newValue(desc, UntypedValue, 0, lvs...) }, diff --git a/prometheus/vec.go b/prometheus/vec.go index a1f3bdf..e6915ee 100644 --- a/prometheus/vec.go +++ b/prometheus/vec.go @@ -14,9 +14,7 @@ package prometheus import ( - "bytes" "fmt" - "hash" "sync" ) @@ -26,16 +24,10 @@ import ( // type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already // provided in this package. type MetricVec struct { - mtx sync.RWMutex // Protects not only children, but also hash and buf. + mtx sync.RWMutex // Protects the children. children map[uint64]Metric desc *Desc - // hash is our own hash instance to avoid repeated allocations. - hash hash.Hash64 - // buf is used to copy string contents into it for hashing, - // again to avoid allocations. - buf bytes.Buffer - newMetric func(labelValues ...string) Metric } @@ -208,30 +200,26 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { if len(vals) != len(m.desc.variableLabels) { return 0, errInconsistentCardinality } - m.hash.Reset() + h := hashNew() for _, val := range vals { - m.buf.Reset() - m.buf.WriteString(val) - m.hash.Write(m.buf.Bytes()) + h = hashAdd(h, val) } - return m.hash.Sum64(), nil + return h, nil } func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { if len(labels) != len(m.desc.variableLabels) { return 0, errInconsistentCardinality } - m.hash.Reset() + h := hashNew() for _, label := range m.desc.variableLabels { val, ok := labels[label] if !ok { return 0, fmt.Errorf("label name %q missing in label map", label) } - m.buf.Reset() - m.buf.WriteString(val) - m.hash.Write(m.buf.Bytes()) + h = hashAdd(h, val) } - return m.hash.Sum64(), nil + return h, nil } func (m *MetricVec) getOrCreateMetric(hash uint64, labelValues ...string) Metric { diff --git a/prometheus/vec_test.go b/prometheus/vec_test.go index 0e9431e..6760ed8 100644 --- a/prometheus/vec_test.go +++ b/prometheus/vec_test.go @@ -14,7 +14,6 @@ package prometheus import ( - "hash/fnv" "testing" ) @@ -23,7 +22,6 @@ func TestDelete(t *testing.T) { vec := MetricVec{ children: map[uint64]Metric{}, desc: desc, - hash: fnv.New64a(), newMetric: func(lvs ...string) Metric { return newValue(desc, UntypedValue, 0, lvs...) }, @@ -63,7 +61,6 @@ func TestDeleteLabelValues(t *testing.T) { vec := MetricVec{ children: map[uint64]Metric{}, desc: desc, - hash: fnv.New64a(), newMetric: func(lvs ...string) Metric { return newValue(desc, UntypedValue, 0, lvs...) },