From c4004ef5f67df09f9c230303a70037a6a716f9aa Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 10 Aug 2016 20:03:15 -0700 Subject: [PATCH] benchmark: measure label resolution in MetricVec Signed-off-by: Stephen J Day --- prometheus/counter.go | 22 ++++------ prometheus/gauge.go | 10 ++--- prometheus/histogram.go | 10 ++--- prometheus/summary.go | 10 ++--- prometheus/untyped.go | 10 ++--- prometheus/vec.go | 10 +++++ prometheus/vec_test.go | 93 ++++++++++++++++++++++++++++++++++------- 7 files changed, 108 insertions(+), 57 deletions(-) diff --git a/prometheus/counter.go b/prometheus/counter.go index 0537f88..7463858 100644 --- a/prometheus/counter.go +++ b/prometheus/counter.go @@ -96,19 +96,15 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { opts.ConstLabels, ) return &CounterVec{ - MetricVec: MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - newMetric: func(lvs ...string) Metric { - result := &counter{value: value{ - desc: desc, - valType: CounterValue, - labelPairs: makeLabelPairs(desc, lvs), - }} - result.init(result) // Init self-collection. - return result - }, - }, + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + result := &counter{value: value{ + desc: desc, + valType: CounterValue, + labelPairs: makeLabelPairs(desc, lvs), + }} + result.init(result) // Init self-collection. + return result + }), } } diff --git a/prometheus/gauge.go b/prometheus/gauge.go index 390c074..af3037d 100644 --- a/prometheus/gauge.go +++ b/prometheus/gauge.go @@ -72,13 +72,9 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { opts.ConstLabels, ) return &GaugeVec{ - MetricVec: MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - newMetric: func(lvs ...string) Metric { - return newValue(desc, GaugeValue, 0, lvs...) - }, - }, + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + return newValue(desc, GaugeValue, 0, lvs...) + }), } } diff --git a/prometheus/histogram.go b/prometheus/histogram.go index 11a9083..3d550ba 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -301,13 +301,9 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec { opts.ConstLabels, ) return &HistogramVec{ - MetricVec: MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - newMetric: func(lvs ...string) Metric { - return newHistogram(desc, opts, lvs...) - }, - }, + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + return newHistogram(desc, opts, lvs...) + }), } } diff --git a/prometheus/summary.go b/prometheus/summary.go index fda213c..2d6739e 100644 --- a/prometheus/summary.go +++ b/prometheus/summary.go @@ -404,13 +404,9 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { opts.ConstLabels, ) return &SummaryVec{ - MetricVec: MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - newMetric: func(lvs ...string) Metric { - return newSummary(desc, opts, lvs...) - }, - }, + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + return newSummary(desc, opts, lvs...) + }), } } diff --git a/prometheus/untyped.go b/prometheus/untyped.go index 89b86ea..b1baf07 100644 --- a/prometheus/untyped.go +++ b/prometheus/untyped.go @@ -70,13 +70,9 @@ func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec { opts.ConstLabels, ) return &UntypedVec{ - MetricVec: MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - newMetric: func(lvs ...string) Metric { - return newValue(desc, UntypedValue, 0, lvs...) - }, - }, + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + return newValue(desc, UntypedValue, 0, lvs...) + }), } } diff --git a/prometheus/vec.go b/prometheus/vec.go index 68f9461..24b1606 100644 --- a/prometheus/vec.go +++ b/prometheus/vec.go @@ -31,6 +31,16 @@ type MetricVec struct { newMetric func(labelValues ...string) Metric } +// newMetricVec returns an initialized MetricVec. The concrete value is +// returned for embedding into another struct. +func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) MetricVec { + return MetricVec{ + children: map[uint64]Metric{}, + desc: desc, + newMetric: newMetric, + } +} + // Describe implements Collector. The length of the returned slice // is always one. func (m *MetricVec) Describe(ch chan<- *Desc) { diff --git a/prometheus/vec_test.go b/prometheus/vec_test.go index 6760ed8..2986de3 100644 --- a/prometheus/vec_test.go +++ b/prometheus/vec_test.go @@ -14,18 +14,12 @@ package prometheus import ( + "fmt" "testing" ) func TestDelete(t *testing.T) { - desc := NewDesc("test", "helpless", []string{"l1", "l2"}, nil) - vec := MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - newMetric: func(lvs ...string) Metric { - return newValue(desc, UntypedValue, 0, lvs...) - }, - } + vec := newUntypedMetricVec("test", "helpless", []string{"l1", "l2"}) if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want { t.Errorf("got %v, want %v", got, want) @@ -57,14 +51,7 @@ func TestDelete(t *testing.T) { } func TestDeleteLabelValues(t *testing.T) { - desc := NewDesc("test", "helpless", []string{"l1", "l2"}, nil) - vec := MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - newMetric: func(lvs ...string) Metric { - return newValue(desc, UntypedValue, 0, lvs...) - }, - } + vec := newUntypedMetricVec("test", "helpless", []string{"l1", "l2"}) if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want { t.Errorf("got %v, want %v", got, want) @@ -86,3 +73,77 @@ func TestDeleteLabelValues(t *testing.T) { t.Errorf("got %v, want %v", got, want) } } + +func newUntypedMetricVec(name, help string, labels []string) *MetricVec { + desc := NewDesc("test", "helpless", labels, nil) + vec := newMetricVec(desc, func(lvs ...string) Metric { + return newValue(desc, UntypedValue, 0, lvs...) + }) + return &vec +} + +func BenchmarkMetricVecWithLabelValuesBasic(B *testing.B) { + benchmarkMetricVecWithLabelValues(B, map[string][]string{ + "l1": []string{"onevalue"}, + "l2": []string{"twovalue"}, + }) +} + +func BenchmarkMetricVecWithLabelValues2Keys10ValueCardinality(B *testing.B) { + benchmarkMetricVecWithLabelValuesCardinality(B, 2, 10) +} + +func BenchmarkMetricVecWithLabelValues4Keys10ValueCardinality(B *testing.B) { + benchmarkMetricVecWithLabelValuesCardinality(B, 4, 10) +} + +func BenchmarkMetricVecWithLabelValues2Keys100ValueCardinality(B *testing.B) { + benchmarkMetricVecWithLabelValuesCardinality(B, 2, 100) +} + +func BenchmarkMetricVecWithLabelValues10Keys100ValueCardinality(B *testing.B) { + benchmarkMetricVecWithLabelValuesCardinality(B, 10, 100) +} + +func BenchmarkMetricVecWithLabelValues10Keys1000ValueCardinality(B *testing.B) { + benchmarkMetricVecWithLabelValuesCardinality(B, 10, 1000) +} + +func benchmarkMetricVecWithLabelValuesCardinality(B *testing.B, nkeys, nvalues int) { + labels := map[string][]string{} + + for i := 0; i < nkeys; i++ { + var ( + k = fmt.Sprintf("key-%v", i) + vs = make([]string, 0, nvalues) + ) + for j := 0; j < nvalues; j++ { + vs = append(vs, fmt.Sprintf("value-%v", j)) + } + labels[k] = vs + } + + benchmarkMetricVecWithLabelValues(B, labels) +} + +func benchmarkMetricVecWithLabelValues(B *testing.B, labels map[string][]string) { + var keys []string + for k := range labels { // map order dependent, who cares though + keys = append(keys, k) + } + + values := make([]string, len(labels)) // value cache for permutations + vec := newUntypedMetricVec("test", "helpless", keys) + + B.ReportAllocs() + B.ResetTimer() + for i := 0; i < B.N; i++ { + // varies input across provide map entries based on key size. + for j, k := range keys { + candidates := labels[k] + values[j] = candidates[i%len(candidates)] + } + + vec.WithLabelValues(values...) + } +}