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:
parent
3d127c266e
commit
a9bdd32c71
|
@ -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)))
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue