From 280eaf33f0e935c18465c7aed3dcdd7a8cdfb6c4 Mon Sep 17 00:00:00 2001 From: Oleg Zaytsev Date: Tue, 24 Aug 2021 07:56:16 +0200 Subject: [PATCH] Use global string map for MatchType.String() (#9237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use global string map for MatchType.String() We were unnecessarily creating a new map for each call of String(). This is a 10x improvement in MatchType.String() performance in time, from 53ns to 4ns on my i7 laptop, and I guess that this method is being called quite often so why throw out the resources. I was surprised that benchmark says that there are no allocations made in the old version. I also tries using `//go:generate stringer` and the result is even better, at about 2.8ns, but having to keep the generated code updated isn't worth the change (at least it's bigger than a small change I was intended to do) Benchmark comparison: name \ time/op old global_map stringer MatchType_String 53.6ns ± 1% 4.1ns ± 1% 2.8ns ± 1% name \ alloc/op old global_map stringer MatchType_String 0.00B 0.00B 0.00B name \ allocs/op old global_map stringer MatchType_String 0.00 0.00 0.00 Old benchmark: goos: darwin goarch: amd64 pkg: github.com/prometheus/prometheus/pkg/labels cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz BenchmarkMatchType_String 21766578 54.36 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 21742339 53.28 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 21985470 53.37 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 21676282 53.50 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 22075573 53.33 ns/op 0 B/op 0 allocs/op PASS ok github.com/prometheus/prometheus/pkg/labels 6.252s New with global map: goos: darwin goarch: amd64 pkg: github.com/prometheus/prometheus/pkg/labels cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz BenchmarkMatchType_String 283412692 4.129 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 294859941 4.091 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 295750158 4.113 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 282827982 4.072 ns/op 0 B/op 0 allocs/op BenchmarkMatchType_String 292942393 4.047 ns/op 0 B/op 0 allocs/op PASS ok github.com/prometheus/prometheus/pkg/labels 8.238s Signed-off-by: Oleg Zaytsev * Use array instead of map Since MatchType is an iota type, we can safely use an array here. This solution is even better: name \ time/op old global_map stringer array MatchType_String 53.6ns ± 1% 4.1ns ± 1% 2.8ns ± 1% 1.0ns ± 1% name \ alloc/op old global_map stringer array MatchType_String 0.00B 0.00B 0.00B 0.00B name \ allocs/op old global_map stringer array MatchType_String 0.00 0.00 0.00 0.00 Signed-off-by: Oleg Zaytsev * Benchmark all MatchType values Co-authored-by: Ganesh Vernekar <15064823+codesome@users.noreply.github.com> Signed-off-by: Oleg Zaytsev * Use constants for limits Signed-off-by: Oleg Zaytsev Co-authored-by: Ganesh Vernekar <15064823+codesome@users.noreply.github.com> Co-authored-by: Ganesh Vernekar <15064823+codesome@users.noreply.github.com> --- pkg/labels/matcher.go | 19 ++++++++++--------- pkg/labels/matcher_test.go | 6 ++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/pkg/labels/matcher.go b/pkg/labels/matcher.go index 88d463233a..f299c40f64 100644 --- a/pkg/labels/matcher.go +++ b/pkg/labels/matcher.go @@ -28,17 +28,18 @@ const ( MatchNotRegexp ) +var matchTypeToStr = [...]string{ + MatchEqual: "=", + MatchNotEqual: "!=", + MatchRegexp: "=~", + MatchNotRegexp: "!~", +} + func (m MatchType) String() string { - typeToStr := map[MatchType]string{ - MatchEqual: "=", - MatchNotEqual: "!=", - MatchRegexp: "=~", - MatchNotRegexp: "!~", + if m < MatchEqual || m > MatchNotRegexp { + panic("unknown match type") } - if str, ok := typeToStr[m]; ok { - return str - } - panic("unknown match type") + return matchTypeToStr[m] } // Matcher models the matching of a label. diff --git a/pkg/labels/matcher_test.go b/pkg/labels/matcher_test.go index 4c85ac2abf..14615a50d1 100644 --- a/pkg/labels/matcher_test.go +++ b/pkg/labels/matcher_test.go @@ -117,3 +117,9 @@ func TestInverse(t *testing.T) { require.Equal(t, test.expected.Type, result.Type) } } + +func BenchmarkMatchType_String(b *testing.B) { + for i := 0; i <= b.N; i++ { + _ = MatchType(i % int(MatchNotRegexp+1)).String() + } +}