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