master
go 99 lines 3.15 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package prometheus
4
5 import (
6 "os"
7 "testing"
8 )
9
10 // pkg/prometheus parses an exposition on every scrape across ~19 importing
11 // collectors, so it is a hot path. Keep these results updated before/after parser
12 // changes so regressions stay visible.
13 //
14 // The payload is the real exposition fixtures the parser tests use:
15 // testdata/testdata.txt (a go-process scrape of gauges/counters/summaries) plus
16 // testdata/histogram-meta.txt (the histogram path), so the benchmark exercises
17 // every assembler branch on representative data.
18 //
19 // Command:
20 //
21 // go test ./pkg/prometheus -run '^$' -bench parseTo -benchmem
22 //
23 // Measured on a developer laptop (Apple M-series, 14 logical CPUs), not CI, so the
24 // absolute numbers are machine-specific — compare relative before/after deltas.
25 //
26 // Legacy fused parser (master, pre-rewrite — captured 2026-06-05):
27 //
28 // parseToMetricFamilies 127868 ns/op 73404 B/op 1314 allocs/op
29 // parseToSeries 111382 ns/op 100444 B/op 1584 allocs/op
30 //
31 // Unified driver+assembler parser (this rewrite — captured 2026-06-05):
32 //
33 // parseToMetricFamilies 133911 ns/op 73408 B/op 1314 allocs/op
34 // parseToSeries 112803 ns/op 100448 B/op 1584 allocs/op
35 //
36 // Scrape (parseToMetricFamilies) and ScrapeSeries keep the legacy allocation profile
37 // (flat allocs); ScrapeSeries is the raw-label path (identical to legacy, essentially
38 // free), Scrape a few percent slower on CPU (per-sample Sample + iterate indirection +
39 // assembler dispatch — makeSample/applySample, inherent to the single-driver model).
40 //
41 // A transform runs with ownLabels=true, so each kept sample owns a copy of its labels
42 // — the cost metric relabeling pays to mutate safely (captured 2026-06-08):
43 //
44 // parseToMetricFamilies 142135 ns/op 73412 B/op 1314 allocs/op
45 // parseToMetricFamiliesWithTransform 147460 ns/op 93940 B/op 1709 allocs/op
46
47 func readBenchData(tb testing.TB) []byte {
48 tb.Helper()
49 var data []byte
50 for _, f := range []string{"testdata/testdata.txt", "testdata/histogram-meta.txt"} {
51 b, err := os.ReadFile(f)
52 if err != nil {
53 tb.Fatal(err)
54 }
55 data = append(data, b...)
56 data = append(data, '\n') // keep files separated when concatenated
57 }
58 return data
59 }
60
61 func BenchmarkPromTextParser_parseToMetricFamilies(b *testing.B) {
62 data := readBenchData(b)
63 var p promTextParser
64 b.ReportAllocs()
65 b.SetBytes(int64(len(data)))
66 b.ResetTimer()
67 for range b.N {
68 if _, err := p.parseToMetricFamilies(data, nil); err != nil {
69 b.Fatal(err)
70 }
71 }
72 }
73
74 func BenchmarkPromTextParser_parseToSeries(b *testing.B) {
75 data := readBenchData(b)
76 var p promTextParser
77 b.ReportAllocs()
78 b.SetBytes(int64(len(data)))
79 b.ResetTimer()
80 for range b.N {
81 if _, err := p.parseToSeries(data); err != nil {
82 b.Fatal(err)
83 }
84 }
85 }
86
87 func BenchmarkPromTextParser_parseToMetricFamiliesWithTransform(b *testing.B) {
88 data := readBenchData(b)
89 var p promTextParser
90 keep := func(s Sample) (Sample, bool, error) { return s, true, nil }
91 b.ReportAllocs()
92 b.SetBytes(int64(len(data)))
93 b.ResetTimer()
94 for range b.N {
95 if _, err := p.parseToMetricFamilies(data, keep); err != nil {
96 b.Fatal(err)
97 }
98 }
99 }