|
|
|
@ -2773,15 +2773,19 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type groupedAggregation struct { |
|
|
|
|
floatValue float64 |
|
|
|
|
histogramValue *histogram.FloatHistogram |
|
|
|
|
floatMean float64 |
|
|
|
|
floatKahanC float64 // "Compensating value" for Kahan summation.
|
|
|
|
|
groupCount float64 |
|
|
|
|
heap vectorByValueHeap |
|
|
|
|
|
|
|
|
|
// All bools together for better packing within the struct.
|
|
|
|
|
seen bool // Was this output groups seen in the input at this timestamp.
|
|
|
|
|
hasFloat bool // Has at least 1 float64 sample aggregated.
|
|
|
|
|
hasHistogram bool // Has at least 1 histogram sample aggregated.
|
|
|
|
|
floatValue float64 |
|
|
|
|
histogramValue *histogram.FloatHistogram |
|
|
|
|
floatMean float64 // Mean, or "compensating value" for Kahan summation.
|
|
|
|
|
groupCount int |
|
|
|
|
groupAggrComplete bool // Used by LIMITK to short-cut series loop when we've reached K elem on every group
|
|
|
|
|
heap vectorByValueHeap |
|
|
|
|
groupAggrComplete bool // Used by LIMITK to short-cut series loop when we've reached K elem on every group.
|
|
|
|
|
incrementalMean bool // True after reverting to incremental calculation of the mean value.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// aggregation evaluates sum, avg, count, stdvar, stddev or quantile at one timestep on inputMatrix.
|
|
|
|
@ -2807,13 +2811,11 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
*group = groupedAggregation{ |
|
|
|
|
seen: true, |
|
|
|
|
floatValue: f, |
|
|
|
|
floatMean: f, |
|
|
|
|
groupCount: 1, |
|
|
|
|
} |
|
|
|
|
switch op { |
|
|
|
|
case parser.AVG: |
|
|
|
|
group.floatMean = f |
|
|
|
|
fallthrough |
|
|
|
|
case parser.SUM: |
|
|
|
|
case parser.AVG, parser.SUM: |
|
|
|
|
if h == nil { |
|
|
|
|
group.hasFloat = true |
|
|
|
|
} else { |
|
|
|
@ -2821,7 +2823,6 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
group.hasHistogram = true |
|
|
|
|
} |
|
|
|
|
case parser.STDVAR, parser.STDDEV: |
|
|
|
|
group.floatMean = f |
|
|
|
|
group.floatValue = 0 |
|
|
|
|
case parser.QUANTILE: |
|
|
|
|
group.heap = make(vectorByValueHeap, 1) |
|
|
|
@ -2847,7 +2848,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
// point in copying the histogram in that case.
|
|
|
|
|
} else { |
|
|
|
|
group.hasFloat = true |
|
|
|
|
group.floatValue, group.floatMean = kahanSumInc(f, group.floatValue, group.floatMean) |
|
|
|
|
group.floatValue, group.floatKahanC = kahanSumInc(f, group.floatValue, group.floatKahanC) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case parser.AVG: |
|
|
|
@ -2855,8 +2856,8 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
if h != nil { |
|
|
|
|
group.hasHistogram = true |
|
|
|
|
if group.histogramValue != nil { |
|
|
|
|
left := h.Copy().Div(float64(group.groupCount)) |
|
|
|
|
right := group.histogramValue.Copy().Div(float64(group.groupCount)) |
|
|
|
|
left := h.Copy().Div(group.groupCount) |
|
|
|
|
right := group.histogramValue.Copy().Div(group.groupCount) |
|
|
|
|
toAdd, err := left.Sub(right) |
|
|
|
|
if err != nil { |
|
|
|
|
handleAggregationError(err, e, inputMatrix[si].Metric.Get(model.MetricNameLabel), &annos) |
|
|
|
@ -2871,6 +2872,22 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
// point in copying the histogram in that case.
|
|
|
|
|
} else { |
|
|
|
|
group.hasFloat = true |
|
|
|
|
if !group.incrementalMean { |
|
|
|
|
newV, newC := kahanSumInc(f, group.floatValue, group.floatKahanC) |
|
|
|
|
if !math.IsInf(newV, 0) { |
|
|
|
|
// The sum doesn't overflow, so we propagate it to the
|
|
|
|
|
// group struct and continue with the regular
|
|
|
|
|
// calculation of the mean value.
|
|
|
|
|
group.floatValue, group.floatKahanC = newV, newC |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
// If we are here, we know that the sum _would_ overflow. So
|
|
|
|
|
// instead of continue to sum up, we revert to incremental
|
|
|
|
|
// calculation of the mean value from here on.
|
|
|
|
|
group.incrementalMean = true |
|
|
|
|
group.floatMean = group.floatValue / (group.groupCount - 1) |
|
|
|
|
group.floatKahanC /= group.groupCount - 1 |
|
|
|
|
} |
|
|
|
|
if math.IsInf(group.floatMean, 0) { |
|
|
|
|
if math.IsInf(f, 0) && (group.floatMean > 0) == (f > 0) { |
|
|
|
|
// The `floatMean` and `s.F` values are `Inf` of the same sign. They
|
|
|
|
@ -2888,8 +2905,13 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Divide each side of the `-` by `group.groupCount` to avoid float64 overflows.
|
|
|
|
|
group.floatMean += f/float64(group.groupCount) - group.floatMean/float64(group.groupCount) |
|
|
|
|
currentMean := group.floatMean + group.floatKahanC |
|
|
|
|
group.floatMean, group.floatKahanC = kahanSumInc( |
|
|
|
|
// Divide each side of the `-` by `group.groupCount` to avoid float64 overflows.
|
|
|
|
|
f/group.groupCount-currentMean/group.groupCount, |
|
|
|
|
group.floatMean, |
|
|
|
|
group.floatKahanC, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case parser.GROUP: |
|
|
|
@ -2912,7 +2934,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
if h == nil { // Ignore native histograms.
|
|
|
|
|
group.groupCount++ |
|
|
|
|
delta := f - group.floatMean |
|
|
|
|
group.floatMean += delta / float64(group.groupCount) |
|
|
|
|
group.floatMean += delta / group.groupCount |
|
|
|
|
group.floatValue += delta * (f - group.floatMean) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2938,20 +2960,23 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
annos.Add(annotations.NewMixedFloatsHistogramsAggWarning(e.Expr.PositionRange())) |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
if aggr.hasHistogram { |
|
|
|
|
switch { |
|
|
|
|
case aggr.hasHistogram: |
|
|
|
|
aggr.histogramValue = aggr.histogramValue.Compact(0) |
|
|
|
|
} else { |
|
|
|
|
aggr.floatValue = aggr.floatMean |
|
|
|
|
case aggr.incrementalMean: |
|
|
|
|
aggr.floatValue = aggr.floatMean + aggr.floatKahanC |
|
|
|
|
default: |
|
|
|
|
aggr.floatValue = (aggr.floatValue + aggr.floatKahanC) / aggr.groupCount |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case parser.COUNT: |
|
|
|
|
aggr.floatValue = float64(aggr.groupCount) |
|
|
|
|
aggr.floatValue = aggr.groupCount |
|
|
|
|
|
|
|
|
|
case parser.STDVAR: |
|
|
|
|
aggr.floatValue /= float64(aggr.groupCount) |
|
|
|
|
aggr.floatValue /= aggr.groupCount |
|
|
|
|
|
|
|
|
|
case parser.STDDEV: |
|
|
|
|
aggr.floatValue = math.Sqrt(aggr.floatValue / float64(aggr.groupCount)) |
|
|
|
|
aggr.floatValue = math.Sqrt(aggr.floatValue / aggr.groupCount) |
|
|
|
|
|
|
|
|
|
case parser.QUANTILE: |
|
|
|
|
aggr.floatValue = quantile(q, aggr.heap) |
|
|
|
@ -2965,7 +2990,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix |
|
|
|
|
if aggr.hasHistogram { |
|
|
|
|
aggr.histogramValue.Compact(0) |
|
|
|
|
} else { |
|
|
|
|
aggr.floatValue += aggr.floatMean // Add Kahan summation compensating term.
|
|
|
|
|
aggr.floatValue += aggr.floatKahanC |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
|
// For other aggregations, we already have the right value.
|
|
|
|
|