diff --git a/model/labels/labels.go b/model/labels/labels.go index bf67224bb..b6663dad2 100644 --- a/model/labels/labels.go +++ b/model/labels/labels.go @@ -342,6 +342,19 @@ func (ls Labels) Validate(f func(l Label) error) error { return nil } +// DropMetricName returns Labels with "__name__" removed. +func (ls Labels) DropMetricName() Labels { + for i, l := range ls { + if l.Name == MetricName { + if i == 0 { // Make common case fast with no allocations. + return ls[1:] + } + return append(ls[:i], ls[i+1:]...) + } + } + return ls +} + // InternStrings calls intern on every string value inside ls, replacing them with what it returns. func (ls *Labels) InternStrings(intern func(string) string) { for i, l := range *ls { diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go index d79a83679..f53c3b2d0 100644 --- a/model/labels/labels_stringlabels.go +++ b/model/labels/labels_stringlabels.go @@ -429,6 +429,27 @@ func (ls Labels) Validate(f func(l Label) error) error { return nil } +// DropMetricName returns Labels with "__name__" removed. +func (ls Labels) DropMetricName() Labels { + for i := 0; i < len(ls.data); { + lName, i2 := decodeString(ls.data, i) + size, i2 := decodeSize(ls.data, i2) + i2 += size + if lName == MetricName { + if i == 0 { // Make common case fast with no allocations. + ls.data = ls.data[i2:] + } else { + ls.data = ls.data[:i] + ls.data[i2:] + } + break + } else if lName[0] > MetricName[0] { // Stop looking if we've gone past. + break + } + i = i2 + } + return ls +} + // InternStrings calls intern on every string value inside ls, replacing them with what it returns. func (ls *Labels) InternStrings(intern func(string) string) { ls.data = intern(ls.data) diff --git a/model/labels/labels_test.go b/model/labels/labels_test.go index 44369278d..c497851d1 100644 --- a/model/labels/labels_test.go +++ b/model/labels/labels_test.go @@ -447,6 +447,12 @@ func TestLabels_Get(t *testing.T) { require.Equal(t, "222", FromStrings("aaaa", "111", "bbb", "222").Get("bbb")) } +func TestLabels_DropMetricName(t *testing.T) { + require.True(t, Equal(FromStrings("aaa", "111", "bbb", "222"), FromStrings("aaa", "111", "bbb", "222").DropMetricName())) + require.True(t, Equal(FromStrings("aaa", "111"), FromStrings(MetricName, "myname", "aaa", "111").DropMetricName())) + require.True(t, Equal(FromStrings("__aaa__", "111", "bbb", "222"), FromStrings("__aaa__", "111", MetricName, "myname", "bbb", "222").DropMetricName())) +} + // BenchmarkLabels_Get was written to check whether a binary search can improve the performance vs the linear search implementation // The results have shown that binary search would only be better when searching last labels in scenarios with more than 10 labels. // In the following list, `old` is the linear search while `new` is the binary search implementation (without calling sort.Search, which performs even worse here) diff --git a/promql/engine.go b/promql/engine.go index 8c8afd181..786b76e1f 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -1076,7 +1076,7 @@ type EvalNodeHelper struct { Out Vector // Caches. - // DropMetricName and label_*. + // label_*. Dmn map[uint64]labels.Labels // funcHistogramQuantile for classic histograms. signatureToMetricWithBuckets map[string]*metricWithBuckets @@ -1101,21 +1101,6 @@ func (enh *EvalNodeHelper) resetBuilder(lbls labels.Labels) { } } -// DropMetricName is a cached version of DropMetricName. -func (enh *EvalNodeHelper) DropMetricName(l labels.Labels) labels.Labels { - if enh.Dmn == nil { - enh.Dmn = make(map[uint64]labels.Labels, len(enh.Out)) - } - h := l.Hash() - ret, ok := enh.Dmn[h] - if ok { - return ret - } - ret = dropMetricName(l) - enh.Dmn[h] = ret - return ret -} - // rangeEval evaluates the given expressions, and then for each step calls // the given funcCall with the values computed for each expression at that // step. The return value is the combination into time series of all the @@ -1492,7 +1477,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio // vector functions, the only change needed is to drop the // metric name in the output. if e.Func.Name != "last_over_time" { - metric = dropMetricName(metric) + metric = metric.DropMetricName() } ss := Series{ Metric: metric, @@ -1624,7 +1609,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio mat := val.(Matrix) if e.Op == parser.SUB { for i := range mat { - mat[i].Metric = dropMetricName(mat[i].Metric) + mat[i].Metric = mat[i].Metric.DropMetricName() for j := range mat[i].Floats { mat[i].Floats[j].F = -mat[i].Floats[j].F } @@ -2370,7 +2355,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * } metric := resultMetric(ls.Metric, rs.Metric, op, matching, enh) if returnBool { - metric = enh.DropMetricName(metric) + metric = metric.DropMetricName() } insertedSigs, exists := matchedSigs[sig] if matching.Card == parser.CardOneToOne { @@ -2492,7 +2477,7 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala lhsSample.F = float lhsSample.H = histogram if shouldDropMetricName(op) || returnBool { - lhsSample.Metric = enh.DropMetricName(lhsSample.Metric) + lhsSample.Metric = lhsSample.Metric.DropMetricName() } enh.Out = append(enh.Out, lhsSample) } @@ -2500,10 +2485,6 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala return enh.Out } -func dropMetricName(l labels.Labels) labels.Labels { - return labels.NewBuilder(l).Del(labels.MetricName).Labels() -} - // scalarBinop evaluates a binary operation between two Scalars. func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 { switch op { diff --git a/promql/functions.go b/promql/functions.go index 64cbd8e0f..aef73e7f2 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -445,7 +445,7 @@ func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper } for _, el := range vec { enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(el.Metric), + Metric: el.Metric.DropMetricName(), F: math.Max(min, math.Min(max, el.F)), }) } @@ -458,7 +458,7 @@ func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel max := vals[1].(Vector)[0].F for _, el := range vec { enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(el.Metric), + Metric: el.Metric.DropMetricName(), F: math.Min(max, el.F), }) } @@ -471,7 +471,7 @@ func funcClampMin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel min := vals[1].(Vector)[0].F for _, el := range vec { enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(el.Metric), + Metric: el.Metric.DropMetricName(), F: math.Max(min, el.F), }) } @@ -493,7 +493,7 @@ func funcRound(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper for _, el := range vec { f := math.Floor(el.F*toNearestInverse+0.5) / toNearestInverse enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(el.Metric), + Metric: el.Metric.DropMetricName(), F: f, }) } @@ -797,7 +797,7 @@ func simpleFunc(vals []parser.Value, enh *EvalNodeHelper, f func(float64) float6 for _, el := range vals[0].(Vector) { if el.H == nil { // Process only float samples. enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(el.Metric), + Metric: el.Metric.DropMetricName(), F: f(el.F), }) } @@ -943,7 +943,7 @@ func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHe vec := vals[0].(Vector) for _, el := range vec { enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(el.Metric), + Metric: el.Metric.DropMetricName(), F: float64(el.T) / 1000, }) } @@ -1057,7 +1057,7 @@ func funcHistogramCount(vals []parser.Value, args parser.Expressions, enh *EvalN continue } enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(sample.Metric), + Metric: sample.Metric.DropMetricName(), F: sample.H.Count, }) } @@ -1074,7 +1074,7 @@ func funcHistogramSum(vals []parser.Value, args parser.Expressions, enh *EvalNod continue } enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(sample.Metric), + Metric: sample.Metric.DropMetricName(), F: sample.H.Sum, }) } @@ -1107,7 +1107,7 @@ func funcHistogramStdDev(vals []parser.Value, args parser.Expressions, enh *Eval variance += cVariance variance /= sample.H.Count enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(sample.Metric), + Metric: sample.Metric.DropMetricName(), F: math.Sqrt(variance), }) } @@ -1140,7 +1140,7 @@ func funcHistogramStdVar(vals []parser.Value, args parser.Expressions, enh *Eval variance += cVariance variance /= sample.H.Count enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(sample.Metric), + Metric: sample.Metric.DropMetricName(), F: variance, }) } @@ -1159,7 +1159,7 @@ func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *Ev continue } enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(sample.Metric), + Metric: sample.Metric.DropMetricName(), F: histogramFraction(lower, upper, sample.H), }) } @@ -1230,7 +1230,7 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev } enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(sample.Metric), + Metric: sample.Metric.DropMetricName(), F: histogramQuantile(q, sample.H), }) } @@ -1440,7 +1440,7 @@ func dateWrapper(vals []parser.Value, enh *EvalNodeHelper, f func(time.Time) flo for _, el := range vals[0].(Vector) { t := time.Unix(int64(el.F), 0).UTC() enh.Out = append(enh.Out, Sample{ - Metric: enh.DropMetricName(el.Metric), + Metric: el.Metric.DropMetricName(), F: f(t), }) }