// Copyright 2017 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package labels import ( "fmt" "io/ioutil" "math/rand" "os" "sort" "testing" "unsafe" "github.com/pkg/errors" promlabels "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/textparse" "github.com/stretchr/testify/require" ) func TestCompareAndEquals(t *testing.T) { cases := []struct { a, b []Label res int }{ { a: []Label{}, b: []Label{}, res: 0, }, { a: []Label{{"a", ""}}, b: []Label{{"a", ""}, {"b", ""}}, res: -1, }, { a: []Label{{"a", ""}}, b: []Label{{"a", ""}}, res: 0, }, { a: []Label{{"aa", ""}, {"aa", ""}}, b: []Label{{"aa", ""}, {"ab", ""}}, res: -1, }, { a: []Label{{"aa", ""}, {"abb", ""}}, b: []Label{{"aa", ""}, {"ab", ""}}, res: 1, }, { a: []Label{ {"__name__", "go_gc_duration_seconds"}, {"job", "prometheus"}, {"quantile", "0.75"}, }, b: []Label{ {"__name__", "go_gc_duration_seconds"}, {"job", "prometheus"}, {"quantile", "1"}, }, res: -1, }, { a: []Label{ {"handler", "prometheus"}, {"instance", "localhost:9090"}, }, b: []Label{ {"handler", "query"}, {"instance", "localhost:9090"}, }, res: -1, }, } for _, c := range cases { // Use constructor to ensure sortedness. a, b := New(c.a...), New(c.b...) require.Equal(t, c.res, Compare(a, b)) require.Equal(t, c.res == 0, a.Equals(b)) } } func BenchmarkSliceSort(b *testing.B) { lbls, err := readPrometheusLabels("../testdata/1m.series", 900000) require.NoError(b, err) for len(lbls) < 20e6 { lbls = append(lbls, lbls...) } for i := range lbls { j := rand.Intn(i + 1) lbls[i], lbls[j] = lbls[j], lbls[i] } for _, k := range []int{ 100, 5000, 50000, 300000, 900000, 5e6, 20e6, } { b.Run(fmt.Sprintf("%d", k), func(b *testing.B) { b.ReportAllocs() for a := 0; a < b.N; a++ { b.StopTimer() cl := make(Slice, k) copy(cl, Slice(lbls[:k])) b.StartTimer() sort.Sort(cl) } }) } } func readPrometheusLabels(fn string, n int) ([]Labels, error) { f, err := os.Open(fn) if err != nil { return nil, err } defer f.Close() b, err := ioutil.ReadAll(f) if err != nil { return nil, err } p := textparse.New(b) i := 0 var mets []Labels hashes := map[uint64]struct{}{} for p.Next() && i < n { m := make(Labels, 0, 10) p.Metric((*promlabels.Labels)(unsafe.Pointer(&m))) h := m.Hash() if _, ok := hashes[h]; ok { continue } mets = append(mets, m) hashes[h] = struct{}{} i++ } if err := p.Err(); err != nil { return nil, err } if i != n { return mets, errors.Errorf("requested %d metrics but found %d", n, i) } return mets, nil } func BenchmarkLabelSetFromMap(b *testing.B) { m := map[string]string{ "job": "node", "instance": "123.123.1.211:9090", "path": "/api/v1/namespaces//deployments/", "method": "GET", "namespace": "system", "status": "500", } var ls Labels b.ReportAllocs() for i := 0; i < b.N; i++ { ls = FromMap(m) } _ = ls } func BenchmarkMapFromLabels(b *testing.B) { m := map[string]string{ "job": "node", "instance": "123.123.1.211:9090", "path": "/api/v1/namespaces//deployments/", "method": "GET", "namespace": "system", "status": "500", } ls := FromMap(m) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { m = ls.Map() } } func BenchmarkLabelSetEquals(b *testing.B) { // The vast majority of comparisons will be against a matching label set. m := map[string]string{ "job": "node", "instance": "123.123.1.211:9090", "path": "/api/v1/namespaces//deployments/", "method": "GET", "namespace": "system", "status": "500", } ls := FromMap(m) var res bool b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { res = ls.Equals(ls) } _ = res } func BenchmarkLabelSetHash(b *testing.B) { // The vast majority of comparisons will be against a matching label set. m := map[string]string{ "job": "node", "instance": "123.123.1.211:9090", "path": "/api/v1/namespaces//deployments/", "method": "GET", "namespace": "system", "status": "500", } ls := FromMap(m) var res uint64 b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { res += ls.Hash() } fmt.Println(res) }