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%
This commit is contained in:
beorn7 2015-02-12 15:22:07 +01:00
parent 3d127c266e
commit a9bdd32c71
2 changed files with 37 additions and 6 deletions

View File

@ -22,6 +22,8 @@ import (
"strings" "strings"
) )
var separator = []byte{0}
// A Metric is similar to a LabelSet, but the key difference is that a Metric is // 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. // a singleton and refers to one and only one stream of samples.
type Metric map[LabelName]LabelValue type Metric map[LabelName]LabelValue
@ -64,23 +66,34 @@ func (m Metric) String() string {
// Fingerprint returns a Metric's Fingerprint. // Fingerprint returns a Metric's Fingerprint.
func (m Metric) Fingerprint() Fingerprint { func (m Metric) Fingerprint() Fingerprint {
labelLength := len(m) labelNames := make([]string, 0, len(m))
labelNames := make([]string, 0, labelLength) maxLength := 0
for labelName := range m { for labelName, labelValue := range m {
labelNames = append(labelNames, string(labelName)) labelNames = append(labelNames, string(labelName))
if len(labelName) > maxLength {
maxLength = len(labelName)
}
if len(labelValue) > maxLength {
maxLength = len(labelValue)
}
} }
sort.Strings(labelNames) sort.Strings(labelNames)
summer := fnv.New64a() summer := fnv.New64a()
buf := make([]byte, maxLength)
for _, labelName := range labelNames { for _, labelName := range labelNames {
labelValue := m[LabelName(labelName)] labelValue := m[LabelName(labelName)]
summer.Write([]byte(labelName)) copy(buf, labelName)
summer.Write([]byte{0}) summer.Write(buf[:len(labelName)])
summer.Write([]byte(labelValue))
summer.Write(separator)
copy(buf, labelValue)
summer.Write(buf[:len(labelValue)])
} }
return Fingerprint(binary.LittleEndian.Uint64(summer.Sum(nil))) return Fingerprint(binary.LittleEndian.Uint64(summer.Sum(nil)))

View File

@ -38,6 +38,24 @@ func testMetric(t testing.TB) {
}, },
fingerprint: 1470933794305433534, 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 { for i, scenario := range scenarios {