github.com/go-graphite/carbonapi@v0.17.0/expr/functions/movingMedian/function_test.go (about)

     1  package movingMedian
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/go-graphite/carbonapi/expr/interfaces"
    10  	"github.com/go-graphite/carbonapi/expr/metadata"
    11  	"github.com/go-graphite/carbonapi/expr/types"
    12  	"github.com/go-graphite/carbonapi/pkg/parser"
    13  	th "github.com/go-graphite/carbonapi/tests"
    14  	"github.com/go-graphite/carbonapi/tests/compare"
    15  )
    16  
    17  var (
    18  	md []interfaces.FunctionMetadata = New("")
    19  )
    20  
    21  func init() {
    22  	for _, m := range md {
    23  		metadata.RegisterFunction(m.Name, m.F)
    24  	}
    25  }
    26  
    27  func TestMovingMedian(t *testing.T) {
    28  	now32 := int64(time.Now().Unix())
    29  
    30  	tests := []th.EvalTestItem{
    31  		{
    32  			"movingMedian(metric1,4)",
    33  			map[parser.MetricRequest][]*types.MetricData{
    34  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 1, 1, 1, 2, 2, 2, 4, 6, 4, 6, 8}, 1, now32)},
    35  			},
    36  			[]*types.MetricData{types.MakeMetricData("movingMedian(metric1,4)", []float64{math.NaN(), math.NaN(), math.NaN(), 1, 1, 1.5, 2, 2, 3, 4, 5, 6},
    37  				1, 0).SetTag("movingMedian", "4").SetNameTag("metric1")}, // StartTime = from
    38  		},
    39  		{
    40  			"movingMedian(metric1,5)",
    41  			map[parser.MetricRequest][]*types.MetricData{
    42  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 1, 1, 1, 2, 2, 2, 4, 6, 4, 6, 8, 1, 2, math.NaN()}, 1, now32)},
    43  			},
    44  			[]*types.MetricData{types.MakeMetricData("movingMedian(metric1,5)", []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1, 1, 2, 2, 2, 4, 4, 6, 6, 4, 2},
    45  				1, 0).SetTag("movingMedian", "5").SetNameTag("metric1")}, // StartTime = from
    46  		},
    47  		{
    48  			"movingMedian(metric1,\"1s\")",
    49  			map[parser.MetricRequest][]*types.MetricData{
    50  				{Metric: "metric1", From: -1, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 1, 1, 1, 1, 2, 2, 2, 4, 6, 4, 6, 8, 1, 2, 0}, 1, now32)},
    51  			},
    52  			[]*types.MetricData{types.MakeMetricData("movingMedian(metric1,'1s')", []float64{1, 1, 1, 1, 2, 2, 2, 4, 6, 4, 6, 8, 1, 2, 0},
    53  				1, 0).SetTag("movingMedian", "'1s'").SetNameTag("metric1")}, // StartTime = from
    54  		},
    55  		{
    56  			"movingMedian(metric1,\"3s\")",
    57  			map[parser.MetricRequest][]*types.MetricData{
    58  				{Metric: "metric1", From: -3, Until: 1}: {types.MakeMetricData("metric1", []float64{0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 4, 6, 4, 6, 8, 1, 2}, 1, now32)},
    59  			},
    60  			[]*types.MetricData{types.MakeMetricData("movingMedian(metric1,'3s')", []float64{0, 1, 1, 1, 1, 2, 2, 2, 4, 4, 6, 6, 6, 2},
    61  				1, 0).SetTag("movingMedian", "'3s'").SetNameTag("metric1")}, // StartTime = from
    62  		},
    63  		{
    64  			"movingMedian(metric1,\"5s\")",
    65  			map[parser.MetricRequest][]*types.MetricData{
    66  				{Metric: "metric1", From: -5, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 2, 3}, 10, now32)}, // step > windowSize
    67  			},
    68  			[]*types.MetricData{types.MakeMetricData("movingMedian(metric1,'5s')", []float64{math.NaN(), math.NaN(), math.NaN()},
    69  				10, now32).SetTag("movingMedian", "'5s'").SetNameTag("metric1")}, // StartTime = from
    70  		},
    71  	}
    72  
    73  	for _, tt := range tests {
    74  		testName := tt.Target
    75  		t.Run(testName, func(t *testing.T) {
    76  			eval := th.EvaluatorFromFunc(md[0].F)
    77  			th.TestEvalExpr(t, eval, &tt)
    78  		})
    79  	}
    80  
    81  }
    82  
    83  func BenchmarkMovingMedian(b *testing.B) {
    84  	benchmarks := []struct {
    85  		target string
    86  		M      map[parser.MetricRequest][]*types.MetricData
    87  	}{
    88  		{
    89  			target: "movingMedian(metric1,'5s')",
    90  			M: map[parser.MetricRequest][]*types.MetricData{
    91  				{Metric: "metric1", From: -5, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 2, 3}, 10, 1)}, // step > windowSize
    92  			},
    93  		},
    94  		{
    95  			target: "movingMedian(metric1,4)",
    96  			M: map[parser.MetricRequest][]*types.MetricData{
    97  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", compare.GenerateMetrics(1024, 1.0, 9.0, 1.0), 1, 1)},
    98  			},
    99  		},
   100  		{
   101  			target: "movingMedian(metric1,2)",
   102  			M: map[parser.MetricRequest][]*types.MetricData{
   103  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", compare.GenerateMetrics(1024, 1.0, 9.0, 1.0), 1, 1)},
   104  			},
   105  		},
   106  		{
   107  			target: "movingMedian(metric1,600)",
   108  			M: map[parser.MetricRequest][]*types.MetricData{
   109  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", compare.GenerateMetrics(1024, 1.0, 9.0, 1.0), 1, 1)},
   110  			},
   111  		},
   112  	}
   113  
   114  	eval := th.EvaluatorFromFunc(md[0].F)
   115  
   116  	for _, bm := range benchmarks {
   117  		b.Run(bm.target, func(b *testing.B) {
   118  			exp, _, err := parser.ParseExpr(bm.target)
   119  			if err != nil {
   120  				b.Fatalf("failed to parse %s: %+v", bm.target, err)
   121  			}
   122  
   123  			b.ResetTimer()
   124  
   125  			for i := 0; i < b.N; i++ {
   126  				g, err := eval.Eval(context.Background(), exp, 0, 1, bm.M)
   127  				if err != nil {
   128  					b.Fatalf("failed to eval %s: %+v", bm.target, err)
   129  				}
   130  				_ = g
   131  			}
   132  		})
   133  	}
   134  }