Skip to content

Commit 849d859

Browse files
author
Bjoern Rabenstein
committed
Add benchmarks to compare text and protobuf parsing.
1 parent 283d371 commit 849d859

File tree

5 files changed

+487
-0
lines changed

5 files changed

+487
-0
lines changed

text/bench_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// Copyright 2015 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package text
15+
16+
import (
17+
"bytes"
18+
"compress/gzip"
19+
"io"
20+
"io/ioutil"
21+
"testing"
22+
23+
dto "github.com/prometheus/client_model/go"
24+
25+
"github.com/matttproud/golang_protobuf_extensions/ext"
26+
)
27+
28+
// Benchmarks to show how much penalty text format parsing accually inflicts.
29+
//
30+
// Example results on Linux 3.13.0, Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz, go1.4.
31+
//
32+
// BenchmarkParseText 1000 1188535 ns/op 205085 B/op 6135 allocs/op
33+
// BenchmarkParseTextGzip 1000 1376567 ns/op 246224 B/op 6151 allocs/op
34+
// BenchmarkParseProto 10000 172790 ns/op 52258 B/op 1160 allocs/op
35+
// BenchmarkParseProtoGzip 5000 324021 ns/op 94931 B/op 1211 allocs/op
36+
// BenchmarkParseProtoMap 10000 187946 ns/op 58714 B/op 1203 allocs/op
37+
//
38+
// CONCLUSION: The overhead for the map is negligible. Text format needs ~5x more allocations.
39+
// Without compression, it needs ~7x longer, but with compression (the more relevant scenario),
40+
// the difference becomes less relevant, only ~4x.
41+
//
42+
// The test data contains 248 samples.
43+
44+
// BenchmarkParseText benchmarks the parsing of a text-format scrape into metric
45+
// family DTOs.
46+
func BenchmarkParseText(b *testing.B) {
47+
b.StopTimer()
48+
data, err := ioutil.ReadFile("testdata/text")
49+
if err != nil {
50+
b.Fatal(err)
51+
}
52+
b.StartTimer()
53+
54+
for i := 0; i < b.N; i++ {
55+
if _, err := parser.TextToMetricFamilies(bytes.NewReader(data)); err != nil {
56+
b.Fatal(err)
57+
}
58+
}
59+
}
60+
61+
// BenchmarkParseTextGzip benchmarks the parsing of a gzipped text-format scrape
62+
// into metric family DTOs.
63+
func BenchmarkParseTextGzip(b *testing.B) {
64+
b.StopTimer()
65+
data, err := ioutil.ReadFile("testdata/text.gz")
66+
if err != nil {
67+
b.Fatal(err)
68+
}
69+
b.StartTimer()
70+
71+
for i := 0; i < b.N; i++ {
72+
in, err := gzip.NewReader(bytes.NewReader(data))
73+
if err != nil {
74+
b.Fatal(err)
75+
}
76+
if _, err := parser.TextToMetricFamilies(in); err != nil {
77+
b.Fatal(err)
78+
}
79+
}
80+
}
81+
82+
// BenchmarkParseProto benchmarks the parsinge of a protobuf-format scrape into
83+
// metric family DTOs. Note that this does not build a map of matric families
84+
// (as the text version does), because it is not required for Prometheus
85+
// ingestion either. (However, it is required for the text-format parsing, as
86+
// the metric family might be sprinkled all over the text, while the
87+
// protobuf-format guarantees bundling at one place.)
88+
func BenchmarkParseProto(b *testing.B) {
89+
b.StopTimer()
90+
data, err := ioutil.ReadFile("testdata/protobuf")
91+
if err != nil {
92+
b.Fatal(err)
93+
}
94+
b.StartTimer()
95+
96+
for i := 0; i < b.N; i++ {
97+
family := &dto.MetricFamily{}
98+
in := bytes.NewReader(data)
99+
for {
100+
family.Reset()
101+
if _, err := ext.ReadDelimited(in, family); err != nil {
102+
if err == io.EOF {
103+
break
104+
}
105+
b.Fatal(err)
106+
}
107+
}
108+
}
109+
}
110+
111+
// BenchmarkParseProtoGzip is like BenchmarkParseProto above, but parses gzipped
112+
// protobuf format.
113+
func BenchmarkParseProtoGzip(b *testing.B) {
114+
b.StopTimer()
115+
data, err := ioutil.ReadFile("testdata/protobuf.gz")
116+
if err != nil {
117+
b.Fatal(err)
118+
}
119+
b.StartTimer()
120+
121+
for i := 0; i < b.N; i++ {
122+
family := &dto.MetricFamily{}
123+
in, err := gzip.NewReader(bytes.NewReader(data))
124+
if err != nil {
125+
b.Fatal(err)
126+
}
127+
for {
128+
family.Reset()
129+
if _, err := ext.ReadDelimited(in, family); err != nil {
130+
if err == io.EOF {
131+
break
132+
}
133+
b.Fatal(err)
134+
}
135+
}
136+
}
137+
}
138+
139+
// BenchmarkParseProtoMap is like BenchmarkParseProto but DOES put the parsed
140+
// metric family DTOs into a map. This is not happening during Prometheus
141+
// ingestion. It is just here to measure the overhead of that map creation and
142+
// separate it from the overhead of the text format parsing.
143+
func BenchmarkParseProtoMap(b *testing.B) {
144+
b.StopTimer()
145+
data, err := ioutil.ReadFile("testdata/protobuf")
146+
if err != nil {
147+
b.Fatal(err)
148+
}
149+
b.StartTimer()
150+
151+
for i := 0; i < b.N; i++ {
152+
families := map[string]*dto.MetricFamily{}
153+
in := bytes.NewReader(data)
154+
for {
155+
family := &dto.MetricFamily{}
156+
if _, err := ext.ReadDelimited(in, family); err != nil {
157+
if err == io.EOF {
158+
break
159+
}
160+
b.Fatal(err)
161+
}
162+
families[family.GetName()] = family
163+
}
164+
}
165+
}

text/testdata/protobuf

8.05 KB
Binary file not shown.

text/testdata/protobuf.gz

2 KB
Binary file not shown.

0 commit comments

Comments
 (0)