Reduce constrainLabels allocations (#1272)

* Add bench

Signed-off-by: Bulat Khasanov <afti@yandex.ru>

* Reduce constrainLabels allocations

Signed-off-by: Bulat Khasanov <afti@yandex.ru>

---------

Signed-off-by: Bulat Khasanov <afti@yandex.ru>
This commit is contained in:
Bulat Khasanov 2023-06-07 10:39:02 +03:00 committed by GitHub
parent 8840afcfc2
commit a09a1d34cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 5 deletions

View File

@ -20,6 +20,24 @@ import (
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
) )
var labelsPool = &sync.Pool{
New: func() interface{} {
return make(Labels)
},
}
func getLabelsFromPool() Labels {
return labelsPool.Get().(Labels)
}
func putLabelsToPool(labels Labels) {
for k := range labels {
delete(labels, k)
}
labelsPool.Put(labels)
}
// MetricVec is a Collector to bundle metrics of the same name that differ in // MetricVec is a Collector to bundle metrics of the same name that differ in
// their label values. MetricVec is not used directly but as a building block // their label values. MetricVec is not used directly but as a building block
// for implementations of vectors of a given metric type, like GaugeVec, // for implementations of vectors of a given metric type, like GaugeVec,
@ -93,6 +111,8 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
// there for pros and cons of the two methods. // there for pros and cons of the two methods.
func (m *MetricVec) Delete(labels Labels) bool { func (m *MetricVec) Delete(labels Labels) bool {
labels = constrainLabels(m.desc, labels) labels = constrainLabels(m.desc, labels)
defer putLabelsToPool(labels)
h, err := m.hashLabels(labels) h, err := m.hashLabels(labels)
if err != nil { if err != nil {
return false return false
@ -109,6 +129,8 @@ func (m *MetricVec) Delete(labels Labels) bool {
// To match curried labels with DeletePartialMatch, it must be called on the base vector. // To match curried labels with DeletePartialMatch, it must be called on the base vector.
func (m *MetricVec) DeletePartialMatch(labels Labels) int { func (m *MetricVec) DeletePartialMatch(labels Labels) int {
labels = constrainLabels(m.desc, labels) labels = constrainLabels(m.desc, labels)
defer putLabelsToPool(labels)
return m.metricMap.deleteByLabels(labels, m.curry) return m.metricMap.deleteByLabels(labels, m.curry)
} }
@ -229,6 +251,8 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
// for example GaugeVec. // for example GaugeVec.
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
labels = constrainLabels(m.desc, labels) labels = constrainLabels(m.desc, labels)
defer putLabelsToPool(labels)
h, err := m.hashLabels(labels) h, err := m.hashLabels(labels)
if err != nil { if err != nil {
return nil, err return nil, err
@ -647,15 +671,16 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
} }
func constrainLabels(desc *Desc, labels Labels) Labels { func constrainLabels(desc *Desc, labels Labels) Labels {
constrainedValues := make(Labels, len(labels)) constrainedLabels := getLabelsFromPool()
for l, v := range labels { for l, v := range labels {
if i, ok := indexOf(l, desc.variableLabels.labelNames()); ok { if i, ok := indexOf(l, desc.variableLabels.labelNames()); ok {
constrainedValues[l] = desc.variableLabels[i].Constrain(v) v = desc.variableLabels[i].Constrain(v)
continue
} }
constrainedValues[l] = v
constrainedLabels[l] = v
} }
return constrainedValues
return constrainedLabels
} }
func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string { func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string {

View File

@ -904,6 +904,13 @@ func testConstrainedCurryVec(t *testing.T, vec *CounterVec, constraint func(stri
}) })
} }
func BenchmarkMetricVecWithBasic(b *testing.B) {
benchmarkMetricVecWith(b, Labels{
"l1": "onevalue",
"l2": "twovalue",
})
}
func BenchmarkMetricVecWithLabelValuesBasic(b *testing.B) { func BenchmarkMetricVecWithLabelValuesBasic(b *testing.B) {
benchmarkMetricVecWithLabelValues(b, map[string][]string{ benchmarkMetricVecWithLabelValues(b, map[string][]string{
"l1": {"onevalue"}, "l1": {"onevalue"},
@ -948,6 +955,27 @@ func benchmarkMetricVecWithLabelValuesCardinality(b *testing.B, nkeys, nvalues i
benchmarkMetricVecWithLabelValues(b, labels) benchmarkMetricVecWithLabelValues(b, labels)
} }
func benchmarkMetricVecWith(b *testing.B, labels map[string]string) {
var keys []string
for k := range labels {
keys = append(keys, k)
}
vec := NewGaugeVec(
GaugeOpts{
Name: "test",
Help: "helpless",
},
keys,
)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
vec.With(labels)
}
}
func benchmarkMetricVecWithLabelValues(b *testing.B, labels map[string][]string) { func benchmarkMetricVecWithLabelValues(b *testing.B, labels map[string][]string) {
var keys []string var keys []string
for k := range labels { // Map order dependent, who cares though. for k := range labels { // Map order dependent, who cares though.