From a9bdd32c7193ea752e863392e37f70d1f6b32bc1 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Thu, 12 Feb 2015 15:22:07 +0100 Subject: [PATCH] Reduce allocations during fingerprinting. Also, add a test to expose https://github.com/prometheus/client_golang/issues/74 . benchmark old ns/op new ns/op delta BenchmarkMetric 7034 6272 -10.83% benchmark old allocs new allocs delta BenchmarkMetric 52 32 -38.46% benchmark old bytes new bytes delta BenchmarkMetric 1976 1800 -8.91% --- model/metric.go | 25 +++++++++++++++++++------ model/metric_test.go | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/model/metric.go b/model/metric.go index 246a562..74f394b 100644 --- a/model/metric.go +++ b/model/metric.go @@ -22,6 +22,8 @@ import ( "strings" ) +var separator = []byte{0} + // A Metric is similar to a LabelSet, but the key difference is that a Metric is // a singleton and refers to one and only one stream of samples. type Metric map[LabelName]LabelValue @@ -64,23 +66,34 @@ func (m Metric) String() string { // Fingerprint returns a Metric's Fingerprint. func (m Metric) Fingerprint() Fingerprint { - labelLength := len(m) - labelNames := make([]string, 0, labelLength) + labelNames := make([]string, 0, len(m)) + maxLength := 0 - for labelName := range m { + for labelName, labelValue := range m { labelNames = append(labelNames, string(labelName)) + if len(labelName) > maxLength { + maxLength = len(labelName) + } + if len(labelValue) > maxLength { + maxLength = len(labelValue) + } } sort.Strings(labelNames) summer := fnv.New64a() + buf := make([]byte, maxLength) for _, labelName := range labelNames { labelValue := m[LabelName(labelName)] - summer.Write([]byte(labelName)) - summer.Write([]byte{0}) - summer.Write([]byte(labelValue)) + copy(buf, labelName) + summer.Write(buf[:len(labelName)]) + + summer.Write(separator) + + copy(buf, labelValue) + summer.Write(buf[:len(labelValue)]) } return Fingerprint(binary.LittleEndian.Uint64(summer.Sum(nil))) diff --git a/model/metric_test.go b/model/metric_test.go index 66a4c8b..7c31bdf 100644 --- a/model/metric_test.go +++ b/model/metric_test.go @@ -38,6 +38,24 @@ func testMetric(t testing.TB) { }, fingerprint: 1470933794305433534, }, + // The following two demonstrate a bug in fingerprinting. They + // should not have the same fingerprint with a sane + // fingerprinting function. See + // https://github.com/prometheus/client_golang/issues/74 . + { + input: Metric{ + "a": "bb", + "b": "c", + }, + fingerprint: 3734646176939799877, + }, + { + input: Metric{ + "a": "b", + "bb": "c", + }, + fingerprint: 3734646176939799877, + }, } for i, scenario := range scenarios {