diff --git a/app/vmselect/promql/binary_op.go b/app/vmselect/promql/binary_op.go index f8ff4c60a..7bde1cab5 100644 --- a/app/vmselect/promql/binary_op.go +++ b/app/vmselect/promql/binary_op.go @@ -260,6 +260,9 @@ func newBinaryOpFunc(bf func(left, right float64, isBool bool) float64) binaryOp dstValues[j] = bf(a, b, isBool) } } + // Optimization: remove time series containing only NaNs. + // This is quite common after applying filters like `q > 0`. + dst = removeNaNs(dst) return dst, nil } } diff --git a/app/vmselect/promql/exec.go b/app/vmselect/promql/exec.go index 42ffbd6ae..c7b0e2f39 100644 --- a/app/vmselect/promql/exec.go +++ b/app/vmselect/promql/exec.go @@ -131,18 +131,23 @@ func timeseriesToResult(tss []*timeseries, maySort bool) ([]netstorage.Result, e func removeNaNs(tss []*timeseries) []*timeseries { rvs := tss[:0] for _, ts := range tss { - nans := 0 + allNans := true for _, v := range ts.Values { - if math.IsNaN(v) { - nans++ + if !math.IsNaN(v) { + allNans = false + break } } - if nans == len(ts.Values) { + if allNans { // Skip timeseries with all NaNs. continue } rvs = append(rvs, ts) } + for i := len(rvs); i < len(tss); i++ { + // Zero unused time series, so GC could reclaim them. + tss[i] = nil + } return rvs }