Skip to content

Commit

Permalink
Replace lookup map with a slice, improve variable names + comments, a…
Browse files Browse the repository at this point in the history
…nd add a benchmark for NewDesc

Signed-off-by: Kyle Eckhart <[email protected]>
  • Loading branch information
kgeckhart committed Feb 19, 2025
1 parent d049c34 commit 6da4db6
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 49 deletions.
18 changes: 7 additions & 11 deletions prometheus/desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,13 @@ type Desc struct {
// variableLabels contains names of labels and normalization function for
// which the metric maintains variable values.
variableLabels *compiledLabels
// variableLabelOrder maps variableLabels indexes to the position in the
// pre-computed labelPairs slice. This allows fast MakeLabelPair function
// that have to place ordered variable label values into pre-sorted labelPairs.
variableLabelOrder []int
// labelPairs contains the sorted DTO label pairs based on the constant labels
// and variable labels
labelPairs []*dto.LabelPair
// variableLabelIndexesInLabelPairs holds all indexes variable labels in the
// labelPairs with the expected index of the variableLabel. This makes it easy
// to identify all variable labels in the labelPairs and where to get their value
// from when given the variable label values
variableLabelIndexesInLabelPairs map[int]int
// id is a hash of the values of the ConstLabels and fqName. This
// must be unique among all registered descriptors and can therefore be
// used as an identifier of the descriptor.
Expand Down Expand Up @@ -179,18 +178,15 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
}
sort.Sort(internal.LabelPairSorter(d.labelPairs))

// In order to facilitate mapping from the unsorted variable labels to
// the sorted variable labels we generate a mapping from output labelPair
// index -> variableLabel index for constructing the final label pairs later
d.variableLabelIndexesInLabelPairs = make(map[int]int, len(d.variableLabels.names))
d.variableLabelOrder = make([]int, len(d.variableLabels.names))
for outputIndex, pair := range d.labelPairs {
// Constant labels have values variable labels do not
// Constant labels have values variable labels do not.
if pair.Value != nil {
continue
}
for sourceIndex, variableLabel := range d.variableLabels.names {
if variableLabel == pair.GetName() {
d.variableLabelIndexesInLabelPairs[outputIndex] = sourceIndex
d.variableLabelOrder[sourceIndex] = outputIndex
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions prometheus/desc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package prometheus

import (
"fmt"
"testing"
)

Expand Down Expand Up @@ -61,3 +62,29 @@ func TestNewInvalidDesc_String(t *testing.T) {
t.Errorf("String: unexpected output: %s", desc.String())
}
}

func BenchmarkNewDesc(b *testing.B) {
for _, bm := range []struct {
labelCount int
descFunc func() *Desc
}{
{
labelCount: 1,
descFunc: new1LabelDescFunc,
},
{
labelCount: 3,
descFunc: new3LabelsDescFunc,
},
{
labelCount: 10,
descFunc: new10LabelsDescFunc,
},
} {
b.Run(fmt.Sprintf("labels=%v", bm.labelCount), func(b *testing.B) {
for i := 0; i < b.N; i++ {
bm.descFunc()
}
})
}
}
12 changes: 7 additions & 5 deletions prometheus/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,20 +221,22 @@ func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
return desc.labelPairs
}
labelPairs := make([]*dto.LabelPair, 0, len(desc.labelPairs))
for i, lp := range desc.labelPairs {
for _, lp := range desc.labelPairs {
var labelToAdd *dto.LabelPair
// Variable labels have no value and need to be inserted with a new dto.LabelPair containing the labelValue
// Variable labels have no value and need to be inserted with a new dto.LabelPair containing the labelValue.
if lp.Value == nil {
variableLabelIndex := desc.variableLabelIndexesInLabelPairs[i]
labelToAdd = &dto.LabelPair{
Name: lp.Name,
Value: proto.String(labelValues[variableLabelIndex]),
Name: lp.Name,
}
} else {
labelToAdd = lp
}
labelPairs = append(labelPairs, labelToAdd)
}
for i, outputIndex := range desc.variableLabelOrder {
labelPairs[outputIndex].Value = proto.String(labelValues[i])
}

return labelPairs
}

Expand Down
105 changes: 72 additions & 33 deletions prometheus/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package prometheus

import (
"fmt"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -192,49 +193,87 @@ func TestMakeLabelPairs(t *testing.T) {
}
}

func Benchmark_MakeLabelPairs(b *testing.B) {
benchFunc := func(desc *Desc, variableLabelValues []string) {
MakeLabelPairs(desc, variableLabelValues)
}
var new1LabelDescFunc = func() *Desc {
return NewDesc(
"metric",
"help",
[]string{"var-label-1"},
Labels{"const-label-1": "value"})
}

var new3LabelsDescFunc = func() *Desc {
return NewDesc(
"metric",
"help",
[]string{"var-label-1", "var-label-3", "var-label-2"},
Labels{"const-label-1": "value", "const-label-3": "value", "const-label-2": "value"})
}

var new10LabelsDescFunc = func() *Desc {
return NewDesc(
"metric",
"help",
[]string{"var-label-5", "var-label-1", "var-label-3", "var-label-2", "var-label-10", "var-label-4", "var-label-7", "var-label-8", "var-label-9", "var-label-6"},
Labels{"const-label-4": "value", "const-label-1": "value", "const-label-7": "value", "const-label-2": "value", "const-label-9": "value", "const-label-8": "value", "const-label-10": "value", "const-label-3": "value", "const-label-6": "value", "const-label-5": "value"})
}

benchmarks := []struct {
name string
bench func(desc *Desc, variableLabelValues []string)
func BenchmarkMakeLabelPairs(b *testing.B) {
for _, bm := range []struct {
desc *Desc
variableLabelValues []string
makeLabelPairValues []string
}{
{
name: "1 label",
desc: NewDesc(
"metric",
"help",
[]string{"var-label-1"},
Labels{"const-label-1": "value"}),
variableLabelValues: []string{"value"},
desc: new1LabelDescFunc(),
makeLabelPairValues: []string{"value"},
},
{
name: "3 labels",
desc: NewDesc(
"metric",
"help",
[]string{"var-label-1", "var-label-3", "var-label-2"},
Labels{"const-label-1": "value", "const-label-3": "value", "const-label-2": "value"}),
variableLabelValues: []string{"value", "value", "value"},
desc: new3LabelsDescFunc(),
makeLabelPairValues: []string{"value", "value", "value"},
},
{
name: "10 labels",
desc: NewDesc(
"metric",
"help",
[]string{"var-label-5", "var-label-1", "var-label-3", "var-label-2", "var-label-10", "var-label-4", "var-label-7", "var-label-8", "var-label-9"},
Labels{"const-label-4": "value", "const-label-1": "value", "const-label-7": "value", "const-label-2": "value", "const-label-9": "value", "const-label-8": "value", "const-label-10": "value", "const-label-3": "value", "const-label-6": "value", "const-label-5": "value"}),
variableLabelValues: []string{"value", "value", "value", "value", "value", "value", "value", "value", "value", "value"},
desc: new10LabelsDescFunc(),
makeLabelPairValues: []string{"value", "value", "value", "value", "value", "value", "value", "value", "value", "value"},
},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
} {
b.Run(fmt.Sprintf("labels=%v", len(bm.makeLabelPairValues)), func(b *testing.B) {
for i := 0; i < b.N; i++ {
benchFunc(bm.desc, bm.variableLabelValues)
MakeLabelPairs(bm.desc, bm.makeLabelPairValues)
}
})
}
}

func BenchmarkConstMetricFlow(b *testing.B) {
for _, bm := range []struct {
descFunc func() *Desc
labelValues []string
}{
{
descFunc: new1LabelDescFunc,
labelValues: []string{"value"},
},
{
descFunc: new3LabelsDescFunc,
labelValues: []string{"value", "value", "value"},
},
{
descFunc: new10LabelsDescFunc,
labelValues: []string{"value", "value", "value", "value", "value", "value", "value", "value", "value", "value"},
},
} {
b.Run(fmt.Sprintf("labels=%v", len(bm.labelValues)), func(b *testing.B) {
for _, metricsToCreate := range []int{1, 2, 3, 5} {
b.Run(fmt.Sprintf("metrics=%v", metricsToCreate), func(b *testing.B) {
for i := 0; i < b.N; i++ {
desc := bm.descFunc()
for j := 0; j < metricsToCreate; j++ {
_, err := NewConstMetric(desc, GaugeValue, 1.0, bm.labelValues...)
if err != nil {
b.Fatal(err)
}
}
}
})
}
})
}
Expand Down

0 comments on commit 6da4db6

Please sign in to comment.