mirror of
https://github.com/prometheus/prometheus.git
synced 2026-02-03 20:39:32 -05:00
[PERF] PromQL: reuse matching data structures in VectorBinop/And/Or/Unless
Signed-off-by: Linas Medziunas <linas.medziunas@gmail.com>
This commit is contained in:
parent
8721871cf7
commit
cde7bbf9e7
1 changed files with 87 additions and 37 deletions
124
promql/engine.go
124
promql/engine.go
|
|
@ -1208,10 +1208,12 @@ type EvalNodeHelper struct {
|
|||
lblResultBuf []byte
|
||||
|
||||
// For binary vector matching.
|
||||
rightSigs map[int]Sample
|
||||
matchedSigs map[int]map[uint64]struct{}
|
||||
resultMetric map[string]labels.Labels
|
||||
numSigs int
|
||||
rightSigs []Sample
|
||||
sigsPresent []bool
|
||||
matchedSigs []map[uint64]struct{}
|
||||
matchedSigsPresent []bool
|
||||
resultMetric map[string]labels.Labels
|
||||
numSigs int
|
||||
|
||||
// For info series matching.
|
||||
rightStrSigs map[string]Sample
|
||||
|
|
@ -1220,6 +1222,44 @@ type EvalNodeHelper struct {
|
|||
enableDelayedNameRemoval bool
|
||||
}
|
||||
|
||||
func (enh *EvalNodeHelper) resetSigsPresent() []bool {
|
||||
if len(enh.sigsPresent) == 0 {
|
||||
enh.sigsPresent = make([]bool, enh.numSigs)
|
||||
} else {
|
||||
clear(enh.sigsPresent)
|
||||
}
|
||||
return enh.sigsPresent
|
||||
}
|
||||
|
||||
func (enh *EvalNodeHelper) resetMatchedSigsPresent() []bool {
|
||||
if len(enh.matchedSigsPresent) == 0 {
|
||||
enh.matchedSigsPresent = make([]bool, enh.numSigs)
|
||||
} else {
|
||||
clear(enh.matchedSigsPresent)
|
||||
}
|
||||
return enh.matchedSigsPresent
|
||||
}
|
||||
|
||||
func (enh *EvalNodeHelper) resetRightSigs() []Sample {
|
||||
if enh.rightSigs == nil {
|
||||
enh.rightSigs = make([]Sample, enh.numSigs)
|
||||
} else {
|
||||
clear(enh.rightSigs)
|
||||
}
|
||||
return enh.rightSigs
|
||||
}
|
||||
|
||||
func (enh *EvalNodeHelper) resetMatchedSigs() []map[uint64]struct{} {
|
||||
if enh.matchedSigs == nil {
|
||||
enh.matchedSigs = make([]map[uint64]struct{}, enh.numSigs)
|
||||
} else {
|
||||
for i := range enh.matchedSigs {
|
||||
clear(enh.matchedSigs[i])
|
||||
}
|
||||
}
|
||||
return enh.matchedSigs
|
||||
}
|
||||
|
||||
func (enh *EvalNodeHelper) resetBuilder(lbls labels.Labels) {
|
||||
if enh.lb == nil {
|
||||
enh.lb = labels.NewBuilder(lbls)
|
||||
|
|
@ -2791,7 +2831,7 @@ func (*evaluator) VectorAnd(lhs, rhs Vector, matching *parser.VectorMatching, lh
|
|||
}
|
||||
|
||||
// Ordinals of signatures present on the right-hand side.
|
||||
rightSigOrdinalsPresent := make([]bool, enh.numSigs)
|
||||
rightSigOrdinalsPresent := enh.resetSigsPresent()
|
||||
for _, sh := range rhsh {
|
||||
rightSigOrdinalsPresent[sh.sigOrdinal] = true
|
||||
}
|
||||
|
|
@ -2817,7 +2857,7 @@ func (*evaluator) VectorOr(lhs, rhs Vector, matching *parser.VectorMatching, lhs
|
|||
return enh.Out
|
||||
}
|
||||
|
||||
leftSigOrdinalsPresent := make([]bool, enh.numSigs)
|
||||
leftSigOrdinalsPresent := enh.resetSigsPresent()
|
||||
// Add everything from the left-hand-side Vector.
|
||||
for i, ls := range lhs {
|
||||
leftSigOrdinalsPresent[lhsh[i].sigOrdinal] = true
|
||||
|
|
@ -2844,7 +2884,7 @@ func (*evaluator) VectorUnless(lhs, rhs Vector, matching *parser.VectorMatching,
|
|||
}
|
||||
|
||||
// Ordinals of signatures present on the right-hand side.
|
||||
rightSigOrdinalsPresent := make([]bool, enh.numSigs)
|
||||
rightSigOrdinalsPresent := enh.resetSigsPresent()
|
||||
for _, sh := range rhsh {
|
||||
rightSigOrdinalsPresent[sh.sigOrdinal] = true
|
||||
}
|
||||
|
|
@ -2876,19 +2916,16 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
}
|
||||
|
||||
// All samples from the rhs by their join signature ordinal.
|
||||
if enh.rightSigs == nil {
|
||||
enh.rightSigs = make(map[int]Sample, len(enh.Out))
|
||||
} else {
|
||||
clear(enh.rightSigs)
|
||||
}
|
||||
rightSigs := enh.rightSigs
|
||||
rightSigs := enh.resetRightSigs()
|
||||
rightSigsPresent := enh.resetSigsPresent()
|
||||
|
||||
// Add all rhs samples to a map so we can easily find matches later.
|
||||
for i, rs := range rhs {
|
||||
sigOrd := rhsh[i].sigOrdinal
|
||||
// The rhs is guaranteed to be the 'one' side. Having multiple samples
|
||||
// with the same signature means that the matching is many-to-many.
|
||||
if duplSample, found := rightSigs[sigOrd]; found {
|
||||
if rightSigsPresent[sigOrd] {
|
||||
duplSample := rightSigs[sigOrd]
|
||||
// oneSide represents which side of the vector represents the 'one' in the many-to-one relationship.
|
||||
oneSide := "right"
|
||||
if matching.Card == parser.CardOneToMany {
|
||||
|
|
@ -2900,16 +2937,22 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
";many-to-many matching not allowed: matching labels must be unique on one side", matchedLabels.String(), oneSide, rs.Metric.String(), duplSample.Metric.String())
|
||||
}
|
||||
rightSigs[sigOrd] = rs
|
||||
rightSigsPresent[sigOrd] = true
|
||||
}
|
||||
|
||||
// Tracks the matching by signature ordinals. For one-to-one operations the value is nil.
|
||||
// For many-to-one the value is a set of hashes to detect duplicated result elements.
|
||||
if enh.matchedSigs == nil {
|
||||
enh.matchedSigs = make(map[int]map[uint64]struct{}, len(rightSigs))
|
||||
var (
|
||||
// Tracks the match-signature for one-to-one operations.
|
||||
matchedSigsPresent []bool
|
||||
|
||||
// Tracks the match-signature for many-to-one operations, the value is a set of signatures
|
||||
// to detect duplicated result elements.
|
||||
matchedSigs []map[uint64]struct{}
|
||||
)
|
||||
if matching.Card == parser.CardOneToOne {
|
||||
matchedSigsPresent = enh.resetMatchedSigsPresent()
|
||||
} else {
|
||||
clear(enh.matchedSigs)
|
||||
matchedSigs = enh.resetMatchedSigs()
|
||||
}
|
||||
matchedSigs := enh.matchedSigs
|
||||
|
||||
var lastErr error
|
||||
|
||||
|
|
@ -2938,26 +2981,26 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
}
|
||||
}
|
||||
|
||||
metric := resultMetric(ls.Metric, rs.Metric, op, matching, enh)
|
||||
if !ev.enableDelayedNameRemoval && returnBool {
|
||||
metric = metric.DropReserved(schema.IsMetadataLabel)
|
||||
}
|
||||
insertedSigs, exists := matchedSigs[sigOrd]
|
||||
dropMetricName := !ev.enableDelayedNameRemoval && returnBool
|
||||
metric := resultMetric(ls.Metric, rs.Metric, op, matching, dropMetricName, enh)
|
||||
|
||||
if matching.Card == parser.CardOneToOne {
|
||||
if exists {
|
||||
if matchedSigsPresent[sigOrd] {
|
||||
ev.errorf("multiple matches for labels: many-to-one matching must be explicit (group_left/group_right)")
|
||||
}
|
||||
matchedSigs[sigOrd] = nil // Set existence to true.
|
||||
matchedSigsPresent[sigOrd] = true
|
||||
} else {
|
||||
// In many-to-one matching the grouping labels have to ensure a unique metric
|
||||
// for the result Vector. Check whether those labels have already been added for
|
||||
// the same matching labels.
|
||||
insertSig := metric.Hash()
|
||||
|
||||
if !exists {
|
||||
insertedSigs = map[uint64]struct{}{}
|
||||
matchedSigs[sigOrd] = insertedSigs
|
||||
} else if _, duplicate := insertedSigs[insertSig]; duplicate {
|
||||
if matchedSigs[sigOrd] == nil {
|
||||
matchedSigs[sigOrd] = map[uint64]struct{}{}
|
||||
}
|
||||
insertedSigs := matchedSigs[sigOrd]
|
||||
|
||||
if _, duplicate := insertedSigs[insertSig]; duplicate {
|
||||
ev.errorf("multiple matches for labels: grouping labels must ensure unique matches")
|
||||
}
|
||||
insertedSigs[insertSig] = struct{}{}
|
||||
|
|
@ -2980,8 +3023,12 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
for i, ls := range lhs {
|
||||
sigOrd := lhsh[i].sigOrdinal
|
||||
|
||||
rs, found := rightSigs[sigOrd] // Look for a match in the rhs Vector.
|
||||
if !found {
|
||||
var rs Sample
|
||||
if rightSigsPresent[sigOrd] {
|
||||
// Found a match in the rhs.
|
||||
rs = rightSigs[sigOrd]
|
||||
} else {
|
||||
// Have to fall back to the fill value.
|
||||
fill := matching.FillValues.RHS
|
||||
if fill == nil {
|
||||
continue
|
||||
|
|
@ -2998,8 +3045,11 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
// For any rhs samples which have not been matched, check if we need to
|
||||
// perform the operation with a fill value from the lhs.
|
||||
if fill := matching.FillValues.LHS; fill != nil {
|
||||
for sigOrd, rs := range rightSigs {
|
||||
if _, matched := matchedSigs[sigOrd]; matched {
|
||||
for i, rs := range rhs {
|
||||
sigOrd := rhsh[i].sigOrdinal
|
||||
|
||||
if (len(matchedSigsPresent) > 0 && matchedSigsPresent[sigOrd]) ||
|
||||
(len(matchedSigs) > 0 && matchedSigs[sigOrd] != nil) {
|
||||
continue // Already matched.
|
||||
}
|
||||
ls := Sample{
|
||||
|
|
@ -3016,7 +3066,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
|
|||
|
||||
// resultMetric returns the metric for the given sample(s) based on the Vector
|
||||
// binary operation and the matching options.
|
||||
func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.VectorMatching, enh *EvalNodeHelper) labels.Labels {
|
||||
func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.VectorMatching, dropMetricName bool, enh *EvalNodeHelper) labels.Labels {
|
||||
if enh.resultMetric == nil {
|
||||
enh.resultMetric = make(map[string]labels.Labels, len(enh.Out))
|
||||
}
|
||||
|
|
@ -3034,7 +3084,7 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V
|
|||
str := string(enh.lblResultBuf)
|
||||
|
||||
enh.resetBuilder(lhs)
|
||||
if changesMetricSchema(op) {
|
||||
if dropMetricName || changesMetricSchema(op) {
|
||||
// Setting empty Metadata causes the deletion of those if they exists.
|
||||
schema.Metadata{}.SetToLabels(enh.lb)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue