From 8f4557b0b14382674d706cc38b673be9e6f1bbd8 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 20 Aug 2024 20:11:21 +0100 Subject: [PATCH 1/4] Scraping benchmark: more realistic test Don't repeat type and help text. Signed-off-by: Bryan Boreham --- scrape/scrape_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scrape/scrape_test.go b/scrape/scrape_test.go index f9164ea7ac..1f0291ae00 100644 --- a/scrape/scrape_test.go +++ b/scrape/scrape_test.go @@ -1256,9 +1256,9 @@ func TestScrapeLoopFailLegacyUnderUTF8(t *testing.T) { func makeTestMetrics(n int) []byte { // Construct a metrics string to parse sb := bytes.Buffer{} + fmt.Fprintf(&sb, "# TYPE metric_a gauge\n") + fmt.Fprintf(&sb, "# HELP metric_a help text\n") for i := 0; i < n; i++ { - fmt.Fprintf(&sb, "# TYPE metric_a gauge\n") - fmt.Fprintf(&sb, "# HELP metric_a help text\n") fmt.Fprintf(&sb, "metric_a{foo=\"%d\",bar=\"%d\"} 1\n", i, i*100) } fmt.Fprintf(&sb, "# EOF\n") From b4ef38cfc8b4ff100783fcc26e909c77993c26f7 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 20 Aug 2024 20:12:02 +0100 Subject: [PATCH 2/4] Scraping: Add benchmark for protobuf format Extract helper function textToProto(). Signed-off-by: Bryan Boreham --- scrape/scrape_test.go | 72 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/scrape/scrape_test.go b/scrape/scrape_test.go index 1f0291ae00..fa11ad6dcf 100644 --- a/scrape/scrape_test.go +++ b/scrape/scrape_test.go @@ -1295,6 +1295,45 @@ func BenchmarkScrapeLoopAppendOM(b *testing.B) { } } +func BenchmarkScrapeLoopAppendProto(b *testing.B) { + ctx, sl := simpleTestScrapeLoop(b) + + slApp := sl.appender(ctx) + + // Construct a metrics string to parse + sb := bytes.Buffer{} + fmt.Fprintf(&sb, "type: GAUGE\n") + fmt.Fprintf(&sb, "help: \"metric_a help text\"\n") + fmt.Fprintf(&sb, "name: \"metric_a\"\n") + for i := 0; i < 100; i++ { + fmt.Fprintf(&sb, `metric: < + label: < + name: "foo" + value: "%d" + > + label: < + name: "bar" + value: "%d" + > + gauge: < + value: 1 + > +> +`, i, i*100) + } + // From text to proto message. + pb := bytes.Buffer{} + require.NoError(b, textToProto(sb.String(), &pb)) + ts := time.Time{} + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + ts = ts.Add(time.Second) + _, _, _, _ = sl.append(slApp, pb.Bytes(), "application/vnd.google.protobuf", ts) + } +} + func TestSetOptionsHandlingStaleness(t *testing.T) { s := teststorage.New(t, 600000) defer s.Close() @@ -2454,18 +2493,7 @@ metric: < buf := &bytes.Buffer{} if test.contentType == "application/vnd.google.protobuf" { - // In case of protobuf, we have to create the binary representation. - pb := &dto.MetricFamily{} - // From text to proto message. - require.NoError(t, proto.UnmarshalText(test.scrapeText, pb)) - // From proto message to binary protobuf. - protoBuf, err := proto.Marshal(pb) - require.NoError(t, err) - - // Write first length, then binary protobuf. - varintBuf := binary.AppendUvarint(nil, uint64(len(protoBuf))) - buf.Write(varintBuf) - buf.Write(protoBuf) + require.NoError(t, textToProto(test.scrapeText, buf)) } else { buf.WriteString(test.scrapeText) } @@ -2480,6 +2508,26 @@ metric: < } } +func textToProto(text string, buf *bytes.Buffer) error { + // In case of protobuf, we have to create the binary representation. + pb := &dto.MetricFamily{} + // From text to proto message. + err := proto.UnmarshalText(text, pb) + if err != nil { + return err + } + // From proto message to binary protobuf. + protoBuf, err := proto.Marshal(pb) + if err != nil { + return err + } + // Write first length, then binary protobuf. + varintBuf := binary.AppendUvarint(nil, uint64(len(protoBuf))) + buf.Write(varintBuf) + buf.Write(protoBuf) + return nil +} + func TestScrapeLoopAppendExemplarSeries(t *testing.T) { scrapeText := []string{`metric_total{n="1"} 1 # {t="1"} 1.0 10000 # EOF`, `metric_total{n="1"} 2 # {t="2"} 2.0 20000 From bc9210e393d520d460dd197bf640578f2daca90a Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 23 Aug 2024 09:12:02 +0100 Subject: [PATCH 3/4] [TESTS] Scrape: make caching work in benchmark Returning 0 from Append means 'unknown', so the series is never cached. Return arbitrary numbers instead. Signed-off-by: Bryan Boreham --- scrape/helpers_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scrape/helpers_test.go b/scrape/helpers_test.go index 12a56d7071..7bc9e3f7d4 100644 --- a/scrape/helpers_test.go +++ b/scrape/helpers_test.go @@ -46,15 +46,15 @@ type nopAppender struct{} func (a nopAppender) SetOptions(opts *storage.AppendOptions) {} func (a nopAppender) Append(storage.SeriesRef, labels.Labels, int64, float64) (storage.SeriesRef, error) { - return 0, nil + return 1, nil } func (a nopAppender) AppendExemplar(storage.SeriesRef, labels.Labels, exemplar.Exemplar) (storage.SeriesRef, error) { - return 0, nil + return 2, nil } func (a nopAppender) AppendHistogram(storage.SeriesRef, labels.Labels, int64, *histogram.Histogram, *histogram.FloatHistogram) (storage.SeriesRef, error) { - return 0, nil + return 3, nil } func (a nopAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { @@ -62,11 +62,11 @@ func (a nopAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, l labels } func (a nopAppender) UpdateMetadata(storage.SeriesRef, labels.Labels, metadata.Metadata) (storage.SeriesRef, error) { - return 0, nil + return 4, nil } func (a nopAppender) AppendCTZeroSample(storage.SeriesRef, labels.Labels, int64, int64) (storage.SeriesRef, error) { - return 0, nil + return 5, nil } func (a nopAppender) Commit() error { return nil } From 281306765e3ddd2b82c0849a9fe443ff2cda88a2 Mon Sep 17 00:00:00 2001 From: bwplotka Date: Sun, 29 Dec 2024 15:10:39 +0000 Subject: [PATCH 4/4] scrape: Unified scrape loop benchmark. Signed-off-by: bwplotka --- scrape/scrape_test.go | 115 ++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/scrape/scrape_test.go b/scrape/scrape_test.go index fa11ad6dcf..a67d52e5cc 100644 --- a/scrape/scrape_test.go +++ b/scrape/scrape_test.go @@ -39,6 +39,7 @@ import ( prom_testutil "github.com/prometheus/client_golang/prometheus/testutil" dto "github.com/prometheus/client_model/go" config_util "github.com/prometheus/common/config" + "github.com/prometheus/common/expfmt" "github.com/prometheus/common/model" "github.com/prometheus/common/promslog" "github.com/stretchr/testify/require" @@ -1265,72 +1266,64 @@ func makeTestMetrics(n int) []byte { return sb.Bytes() } +func promTextToProto(tb testing.TB, text []byte) []byte { + tb.Helper() + + d := expfmt.NewDecoder(bytes.NewReader(text), expfmt.TextVersion) + + pb := &dto.MetricFamily{} + if err := d.Decode(pb); err != nil { + tb.Fatal(err) + } + o, err := proto.Marshal(pb) + if err != nil { + tb.Fatal(err) + } + buf := bytes.Buffer{} + // Write first length, then binary protobuf. + varintBuf := binary.AppendUvarint(nil, uint64(len(o))) + buf.Write(varintBuf) + buf.Write(o) + return buf.Bytes() +} + +/* + export bench=scrape-loop-v1 && go test \ + -run '^$' -bench '^BenchmarkScrapeLoopAppend' \ + -benchtime 5s -count 6 -cpu 2 -timeout 999m \ + | tee ${bench}.txt +*/ func BenchmarkScrapeLoopAppend(b *testing.B) { - ctx, sl := simpleTestScrapeLoop(b) + metricsText := makeTestMetrics(100) - slApp := sl.appender(ctx) - metrics := makeTestMetrics(100) - ts := time.Time{} + // Create proto representation. + metricsProto := promTextToProto(b, metricsText) - b.ResetTimer() + for _, bcase := range []struct { + name string + contentType string + parsable []byte + }{ + {name: "PromText", contentType: "text/plain", parsable: metricsText}, + {name: "OMText", contentType: "application/openmetrics-text", parsable: metricsText}, + {name: "PromProto", contentType: "application/vnd.google.protobuf", parsable: metricsProto}, + } { + b.Run(fmt.Sprintf("fmt=%v", bcase.name), func(b *testing.B) { + ctx, sl := simpleTestScrapeLoop(b) - for i := 0; i < b.N; i++ { - ts = ts.Add(time.Second) - _, _, _, _ = sl.append(slApp, metrics, "text/plain", ts) - } -} + slApp := sl.appender(ctx) + ts := time.Time{} -func BenchmarkScrapeLoopAppendOM(b *testing.B) { - ctx, sl := simpleTestScrapeLoop(b) - - slApp := sl.appender(ctx) - metrics := makeTestMetrics(100) - ts := time.Time{} - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - ts = ts.Add(time.Second) - _, _, _, _ = sl.append(slApp, metrics, "application/openmetrics-text", ts) - } -} - -func BenchmarkScrapeLoopAppendProto(b *testing.B) { - ctx, sl := simpleTestScrapeLoop(b) - - slApp := sl.appender(ctx) - - // Construct a metrics string to parse - sb := bytes.Buffer{} - fmt.Fprintf(&sb, "type: GAUGE\n") - fmt.Fprintf(&sb, "help: \"metric_a help text\"\n") - fmt.Fprintf(&sb, "name: \"metric_a\"\n") - for i := 0; i < 100; i++ { - fmt.Fprintf(&sb, `metric: < - label: < - name: "foo" - value: "%d" - > - label: < - name: "bar" - value: "%d" - > - gauge: < - value: 1 - > -> -`, i, i*100) - } - // From text to proto message. - pb := bytes.Buffer{} - require.NoError(b, textToProto(sb.String(), &pb)) - ts := time.Time{} - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - ts = ts.Add(time.Second) - _, _, _, _ = sl.append(slApp, pb.Bytes(), "application/vnd.google.protobuf", ts) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + ts = ts.Add(time.Second) + _, _, _, err := sl.append(slApp, bcase.parsable, bcase.contentType, ts) + if err != nil { + b.Fatal(err) + } + } + }) } }