promql: make avg_over_time faster and more precise

Same idea as for the avg aggregator before: Most of the time, there is
no overflow, so we don't have to revert to the more expensive and less
precise incremental calculation of the mean value.

Signed-off-by: beorn7 <beorn@grafana.com>
pull/14413/head
beorn7 2024-07-04 18:47:52 +02:00
parent 3a908d8e08
commit cff0429b1a
2 changed files with 28 additions and 8 deletions

View File

@ -573,9 +573,28 @@ func funcAvgOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
return vec, nil return vec, nil
} }
return aggrOverTime(vals, enh, func(s Series) float64 { return aggrOverTime(vals, enh, func(s Series) float64 {
var mean, count, c float64 var (
sum, mean, count, kahanC float64
incrementalMean bool
)
for _, f := range s.Floats { for _, f := range s.Floats {
count++ count++
if !incrementalMean {
newSum, newC := kahanSumInc(f.F, sum, kahanC)
// Perform regular mean calculation as long as
// the sum doesn't overflow and (in any case)
// for the first iteration (even if we start
// with ±Inf) to not run into division-by-zero
// problems below.
if count == 1 || !math.IsInf(newSum, 0) {
sum, kahanC = newSum, newC
continue
}
// Handle overflow by reverting to incremental calculation of the mean value.
incrementalMean = true
mean = sum / (count - 1)
kahanC /= count - 1
}
if math.IsInf(mean, 0) { if math.IsInf(mean, 0) {
if math.IsInf(f.F, 0) && (mean > 0) == (f.F > 0) { if math.IsInf(f.F, 0) && (mean > 0) == (f.F > 0) {
// The `mean` and `f.F` values are `Inf` of the same sign. They // The `mean` and `f.F` values are `Inf` of the same sign. They
@ -593,14 +612,13 @@ func funcAvgOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
continue continue
} }
} }
correctedMean := mean + c correctedMean := mean + kahanC
mean, c = kahanSumInc(f.F/count-correctedMean/count, mean, c) mean, kahanC = kahanSumInc(f.F/count-correctedMean/count, mean, kahanC)
} }
if incrementalMean {
if math.IsInf(mean, 0) { return mean + kahanC
return mean
} }
return mean + c return (sum + kahanC) / count
}), nil }), nil
} }

View File

@ -737,7 +737,6 @@ eval instant at 1m avg_over_time(metric6c[1m])
eval instant at 1m sum_over_time(metric6c[1m])/count_over_time(metric6c[1m]) eval instant at 1m sum_over_time(metric6c[1m])/count_over_time(metric6c[1m])
{} NaN {} NaN
eval instant at 1m avg_over_time(metric7[1m]) eval instant at 1m avg_over_time(metric7[1m])
{} NaN {} NaN
@ -772,6 +771,9 @@ load 10s
eval instant at 1m sum_over_time(metric[1m]) eval instant at 1m sum_over_time(metric[1m])
{} 2 {} 2
eval instant at 1m avg_over_time(metric[1m])
{} 0.5
# Tests for stddev_over_time and stdvar_over_time. # Tests for stddev_over_time and stdvar_over_time.
clear clear
load 10s load 10s