Skip to content

Commit

Permalink
Optimize dpgSignature function in Prometheus receiver
Browse files Browse the repository at this point in the history
This function is invoked very often, which leads to high CPU usage when
collecting Prometheus metrics.

Benchmark results on my machine show 3x less CPU and almost 4x less
memory used. Since I'm also changing the format of returned values
(dropping superfluous characters), it will reduce overall memory usage
of the receiver, because they are used as map keys.

Benchmark result before this change:

Benchmark_dpgSignature-6   	 1284531	       969.6 ns/op	     120 B/op	       7 allocs/op

Benchmark result after this change:

Benchmark_dpgSignature-6   	 3515179	       313.9 ns/op	      32 B/op	       2 allocs/op
  • Loading branch information
x13n committed Apr 15, 2021
1 parent 9cf978e commit 8df19b8
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 9 deletions.
18 changes: 15 additions & 3 deletions receiver/prometheusreceiver/internal/metricsbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,27 @@ func isUsefulLabel(mType metricspb.MetricDescriptor_Type, labelKey string) bool

// dpgSignature is used to create a key for data complexValue belong to a same group of a metric family
func dpgSignature(orderedKnownLabelKeys []string, ls labels.Labels) string {
sign := make([]string, 0, len(orderedKnownLabelKeys))
size := 0
for _, k := range orderedKnownLabelKeys {
v := ls.Get(k)
if v == "" {
continue
}
sign = append(sign, k+"="+v)
// 2 enclosing quotes + 1 equality sign = 3 extra chars.
// Note: if any character in the label value requires escaping,
// we'll need more space than that, which will lead to some
// extra allocation.
size += 3 + len(k) + len(v)
}
return fmt.Sprintf("%#v", sign)
sign := make([]byte, 0, size)
for _, k := range orderedKnownLabelKeys {
v := ls.Get(k)
if v == "" {
continue
}
sign = strconv.AppendQuote(sign, k+"="+v)
}
return string(sign)
}

func normalizeMetricName(name string) string {
Expand Down
22 changes: 16 additions & 6 deletions receiver/prometheusreceiver/internal/metricsbuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package internal

import (
"reflect"
"runtime"
"testing"

metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
Expand Down Expand Up @@ -1205,6 +1206,15 @@ func Test_isUsefulLabel(t *testing.T) {
}
}

func Benchmark_dpgSignature(b *testing.B) {
knownLabelKeys := []string{"a", "b"}
labels := labels.FromStrings("a", "va", "b", "vb", "x", "xa")
b.ReportAllocs()
for i := 0; i < b.N; i++ {
runtime.KeepAlive(dpgSignature(knownLabelKeys, labels))
}
}

func Test_dpgSignature(t *testing.T) {
knownLabelKeys := []string{"a", "b"}

Expand All @@ -1213,16 +1223,16 @@ func Test_dpgSignature(t *testing.T) {
ls labels.Labels
want string
}{
{"1st label", labels.FromStrings("a", "va"), `[]string{"a=va"}`},
{"2nd label", labels.FromStrings("b", "vb"), `[]string{"b=vb"}`},
{"two labels", labels.FromStrings("a", "va", "b", "vb"), `[]string{"a=va", "b=vb"}`},
{"extra label", labels.FromStrings("a", "va", "b", "vb", "x", "xa"), `[]string{"a=va", "b=vb"}`},
{"different order", labels.FromStrings("b", "vb", "a", "va"), `[]string{"a=va", "b=vb"}`},
{"1st label", labels.FromStrings("a", "va"), `"a=va"`},
{"2nd label", labels.FromStrings("b", "vb"), `"b=vb"`},
{"two labels", labels.FromStrings("a", "va", "b", "vb"), `"a=va""b=vb"`},
{"extra label", labels.FromStrings("a", "va", "b", "vb", "x", "xa"), `"a=va""b=vb"`},
{"different order", labels.FromStrings("b", "vb", "a", "va"), `"a=va""b=vb"`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := dpgSignature(knownLabelKeys, tt.ls); got != tt.want {
t.Errorf("dpgSignature() = %v, want %v", got, tt.want)
t.Errorf("dpgSignature() = %q, want %q", got, tt.want)
}
})
}
Expand Down

0 comments on commit 8df19b8

Please sign in to comment.