...

Source file src/github.com/prometheus/common/expfmt/bench_test.go

Documentation: github.com/prometheus/common/expfmt

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

View as plain text