mirror of https://github.com/prometheus/prometheus
Labels: Add DropMetricName function, used in PromQL (#13446)
This function is called very frequently when executing PromQL functions, and we can do it much more efficiently inside Labels. In the common case that `__name__` comes first in the labels, we simply re-point to start at the next label, which is nearly free. `DropMetricName` is now so cheap I removed the cache - benchmarks show everything still goes faster. Signed-off-by: Bryan Boreham <bjboreham@gmail.com>pull/13462/head
parent
3f30ad3cc2
commit
74b73d1e2c
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue