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
|
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.
|
// InternStrings calls intern on every string value inside ls, replacing them with what it returns.
|
||||||
func (ls *Labels) InternStrings(intern func(string) string) {
|
func (ls *Labels) InternStrings(intern func(string) string) {
|
||||||
for i, l := range *ls {
|
for i, l := range *ls {
|
||||||
|
|
|
@ -429,6 +429,27 @@ func (ls Labels) Validate(f func(l Label) error) error {
|
||||||
return nil
|
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.
|
// InternStrings calls intern on every string value inside ls, replacing them with what it returns.
|
||||||
func (ls *Labels) InternStrings(intern func(string) string) {
|
func (ls *Labels) InternStrings(intern func(string) string) {
|
||||||
ls.data = intern(ls.data)
|
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"))
|
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
|
// 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.
|
// 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)
|
// 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
|
Out Vector
|
||||||
|
|
||||||
// Caches.
|
// Caches.
|
||||||
// DropMetricName and label_*.
|
// label_*.
|
||||||
Dmn map[uint64]labels.Labels
|
Dmn map[uint64]labels.Labels
|
||||||
// funcHistogramQuantile for classic histograms.
|
// funcHistogramQuantile for classic histograms.
|
||||||
signatureToMetricWithBuckets map[string]*metricWithBuckets
|
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
|
// rangeEval evaluates the given expressions, and then for each step calls
|
||||||
// the given funcCall with the values computed for each expression at that
|
// 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
|
// 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
|
// vector functions, the only change needed is to drop the
|
||||||
// metric name in the output.
|
// metric name in the output.
|
||||||
if e.Func.Name != "last_over_time" {
|
if e.Func.Name != "last_over_time" {
|
||||||
metric = dropMetricName(metric)
|
metric = metric.DropMetricName()
|
||||||
}
|
}
|
||||||
ss := Series{
|
ss := Series{
|
||||||
Metric: metric,
|
Metric: metric,
|
||||||
|
@ -1624,7 +1609,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotatio
|
||||||
mat := val.(Matrix)
|
mat := val.(Matrix)
|
||||||
if e.Op == parser.SUB {
|
if e.Op == parser.SUB {
|
||||||
for i := range mat {
|
for i := range mat {
|
||||||
mat[i].Metric = dropMetricName(mat[i].Metric)
|
mat[i].Metric = mat[i].Metric.DropMetricName()
|
||||||
for j := range mat[i].Floats {
|
for j := range mat[i].Floats {
|
||||||
mat[i].Floats[j].F = -mat[i].Floats[j].F
|
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)
|
metric := resultMetric(ls.Metric, rs.Metric, op, matching, enh)
|
||||||
if returnBool {
|
if returnBool {
|
||||||
metric = enh.DropMetricName(metric)
|
metric = metric.DropMetricName()
|
||||||
}
|
}
|
||||||
insertedSigs, exists := matchedSigs[sig]
|
insertedSigs, exists := matchedSigs[sig]
|
||||||
if matching.Card == parser.CardOneToOne {
|
if matching.Card == parser.CardOneToOne {
|
||||||
|
@ -2492,7 +2477,7 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala
|
||||||
lhsSample.F = float
|
lhsSample.F = float
|
||||||
lhsSample.H = histogram
|
lhsSample.H = histogram
|
||||||
if shouldDropMetricName(op) || returnBool {
|
if shouldDropMetricName(op) || returnBool {
|
||||||
lhsSample.Metric = enh.DropMetricName(lhsSample.Metric)
|
lhsSample.Metric = lhsSample.Metric.DropMetricName()
|
||||||
}
|
}
|
||||||
enh.Out = append(enh.Out, lhsSample)
|
enh.Out = append(enh.Out, lhsSample)
|
||||||
}
|
}
|
||||||
|
@ -2500,10 +2485,6 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala
|
||||||
return enh.Out
|
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.
|
// scalarBinop evaluates a binary operation between two Scalars.
|
||||||
func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 {
|
func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 {
|
||||||
switch op {
|
switch op {
|
||||||
|
|
|
@ -445,7 +445,7 @@ func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper
|
||||||
}
|
}
|
||||||
for _, el := range vec {
|
for _, el := range vec {
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(el.Metric),
|
Metric: el.Metric.DropMetricName(),
|
||||||
F: math.Max(min, math.Min(max, el.F)),
|
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
|
max := vals[1].(Vector)[0].F
|
||||||
for _, el := range vec {
|
for _, el := range vec {
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(el.Metric),
|
Metric: el.Metric.DropMetricName(),
|
||||||
F: math.Min(max, el.F),
|
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
|
min := vals[1].(Vector)[0].F
|
||||||
for _, el := range vec {
|
for _, el := range vec {
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(el.Metric),
|
Metric: el.Metric.DropMetricName(),
|
||||||
F: math.Max(min, el.F),
|
F: math.Max(min, el.F),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -493,7 +493,7 @@ func funcRound(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper
|
||||||
for _, el := range vec {
|
for _, el := range vec {
|
||||||
f := math.Floor(el.F*toNearestInverse+0.5) / toNearestInverse
|
f := math.Floor(el.F*toNearestInverse+0.5) / toNearestInverse
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(el.Metric),
|
Metric: el.Metric.DropMetricName(),
|
||||||
F: f,
|
F: f,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -797,7 +797,7 @@ func simpleFunc(vals []parser.Value, enh *EvalNodeHelper, f func(float64) float6
|
||||||
for _, el := range vals[0].(Vector) {
|
for _, el := range vals[0].(Vector) {
|
||||||
if el.H == nil { // Process only float samples.
|
if el.H == nil { // Process only float samples.
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(el.Metric),
|
Metric: el.Metric.DropMetricName(),
|
||||||
F: f(el.F),
|
F: f(el.F),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -943,7 +943,7 @@ func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHe
|
||||||
vec := vals[0].(Vector)
|
vec := vals[0].(Vector)
|
||||||
for _, el := range vec {
|
for _, el := range vec {
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(el.Metric),
|
Metric: el.Metric.DropMetricName(),
|
||||||
F: float64(el.T) / 1000,
|
F: float64(el.T) / 1000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1057,7 +1057,7 @@ func funcHistogramCount(vals []parser.Value, args parser.Expressions, enh *EvalN
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(sample.Metric),
|
Metric: sample.Metric.DropMetricName(),
|
||||||
F: sample.H.Count,
|
F: sample.H.Count,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1074,7 +1074,7 @@ func funcHistogramSum(vals []parser.Value, args parser.Expressions, enh *EvalNod
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(sample.Metric),
|
Metric: sample.Metric.DropMetricName(),
|
||||||
F: sample.H.Sum,
|
F: sample.H.Sum,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1107,7 +1107,7 @@ func funcHistogramStdDev(vals []parser.Value, args parser.Expressions, enh *Eval
|
||||||
variance += cVariance
|
variance += cVariance
|
||||||
variance /= sample.H.Count
|
variance /= sample.H.Count
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(sample.Metric),
|
Metric: sample.Metric.DropMetricName(),
|
||||||
F: math.Sqrt(variance),
|
F: math.Sqrt(variance),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1140,7 +1140,7 @@ func funcHistogramStdVar(vals []parser.Value, args parser.Expressions, enh *Eval
|
||||||
variance += cVariance
|
variance += cVariance
|
||||||
variance /= sample.H.Count
|
variance /= sample.H.Count
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(sample.Metric),
|
Metric: sample.Metric.DropMetricName(),
|
||||||
F: variance,
|
F: variance,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1159,7 +1159,7 @@ func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *Ev
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(sample.Metric),
|
Metric: sample.Metric.DropMetricName(),
|
||||||
F: histogramFraction(lower, upper, sample.H),
|
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{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(sample.Metric),
|
Metric: sample.Metric.DropMetricName(),
|
||||||
F: histogramQuantile(q, sample.H),
|
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) {
|
for _, el := range vals[0].(Vector) {
|
||||||
t := time.Unix(int64(el.F), 0).UTC()
|
t := time.Unix(int64(el.F), 0).UTC()
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: enh.DropMetricName(el.Metric),
|
Metric: el.Metric.DropMetricName(),
|
||||||
F: f(t),
|
F: f(t),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue