...

Source file src/github.com/PaesslerAG/gval/benchmarks_test.go

Documentation: github.com/PaesslerAG/gval

     1  package gval
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  )
     7  
     8  func BenchmarkGval(bench *testing.B) {
     9  	benchmarks := []evaluationTest{
    10  		{
    11  			// Serves as a "water test" to give an idea of the general overhead
    12  			name:       "const",
    13  			expression: "1",
    14  		},
    15  		{
    16  			name:       "single parameter",
    17  			expression: "requests_made",
    18  			parameter: map[string]interface{}{
    19  				"requests_made": 99.0,
    20  			},
    21  		},
    22  		{
    23  			name:       "parameter",
    24  			expression: "requests_made > requests_succeeded",
    25  			parameter: map[string]interface{}{
    26  				"requests_made":      99.0,
    27  				"requests_succeeded": 90.0,
    28  			},
    29  		},
    30  		{
    31  			// The most common use case, a single variable, modified slightly, compared to a constant.
    32  			// This is the "expected" use case.
    33  			name:       "common",
    34  			expression: "(requests_made * requests_succeeded / 100) >= 90",
    35  			parameter: map[string]interface{}{
    36  				"requests_made":      99.0,
    37  				"requests_succeeded": 90.0,
    38  			},
    39  		},
    40  		{
    41  			// All major possibilities in one expression.
    42  			name: "complex",
    43  			expression: `2 > 1 &&
    44  			"something" != "nothing" ||
    45  			date("2014-01-20") < date("Wed Jul  8 23:07:35 MDT 2015") && 
    46  			object["Variable name with spaces"] <= array[0] &&
    47  			modifierTest + 1000 / 2 > (80 * 100 % 2)`,
    48  			parameter: map[string]interface{}{
    49  				"object":       map[string]interface{}{"Variable name with spaces": 10.},
    50  				"array":        []interface{}{0.},
    51  				"modifierTest": 7.3,
    52  			},
    53  		},
    54  		{
    55  			// no variables, no modifiers
    56  			name:       "literal",
    57  			expression: "(2) > (1)",
    58  		},
    59  		{
    60  			name:       "modifier",
    61  			expression: "(2) + (2) == (4)",
    62  		},
    63  		{
    64  			//   Benchmarks uncompiled parameter regex operators, which are the most expensive of the lot.
    65  			//   Note that regex compilation times are unpredictable and wily things. The regex engine has a lot of edge cases
    66  			//   and possible performance pitfalls. This test doesn't aim to be comprehensive against all possible regex scenarios,
    67  			//   it is primarily concerned with tracking how much longer it takes to compile a regex at evaluation-time than during parse-time.
    68  			name:       "regex",
    69  			expression: "(foo !~ bar) && (foo + bar =~ oba)",
    70  			parameter: map[string]interface{}{
    71  				"foo": "foo",
    72  				"bar": "bar",
    73  				"baz": "baz",
    74  				"oba": ".*oba.*",
    75  			},
    76  		},
    77  		{
    78  			// Benchmarks pre-compilable regex patterns. Meant to serve as a sanity check that constant strings used as regex patterns
    79  			// are actually being precompiled.
    80  			// Also demonstrates that (generally) compiling a regex at evaluation-time takes an order of magnitude more time than pre-compiling.
    81  			name:       "constant regex",
    82  			expression: `(foo !~ "[bB]az") && (bar =~ "[bB]ar")`,
    83  			parameter: map[string]interface{}{
    84  				"foo": "foo",
    85  				"bar": "bar",
    86  				"baz": "baz",
    87  				"oba": ".*oba.*",
    88  			},
    89  		},
    90  		{
    91  			name:       "accessors",
    92  			expression: "foo.Int",
    93  			parameter:  fooFailureParameters,
    94  		},
    95  		{
    96  			name:       "accessors method",
    97  			expression: "foo.Func()",
    98  			parameter:  fooFailureParameters,
    99  		},
   100  		{
   101  			name:       "accessors method parameter",
   102  			expression: `foo.FuncArgStr("bonk")`,
   103  			parameter:  fooFailureParameters,
   104  		},
   105  		{
   106  			name:       "nested accessors",
   107  			expression: `foo.Nested.Funk`,
   108  			parameter:  fooFailureParameters,
   109  		},
   110  		{
   111  			name:       "decimal arithmetic",
   112  			expression: "(requests_made * requests_succeeded / 100)",
   113  			extension:  decimalArithmetic,
   114  			parameter: map[string]interface{}{
   115  				"requests_made":      99.0,
   116  				"requests_succeeded": 90.0,
   117  			},
   118  		},
   119  		{
   120  			name:       "decimal logic",
   121  			expression: "(requests_made * requests_succeeded / 100) >= 90",
   122  			extension:  decimalArithmetic,
   123  			parameter: map[string]interface{}{
   124  				"requests_made":      99.0,
   125  				"requests_succeeded": 90.0,
   126  			},
   127  		},
   128  	}
   129  	for _, benchmark := range benchmarks {
   130  		eval, err := Full().NewEvaluable(benchmark.expression)
   131  		if err != nil {
   132  			bench.Fatal(err)
   133  		}
   134  		_, err = eval(context.Background(), benchmark.parameter)
   135  		if err != nil {
   136  			bench.Fatal(err)
   137  		}
   138  		bench.Run(benchmark.name+"_evaluation", func(bench *testing.B) {
   139  			for i := 0; i < bench.N; i++ {
   140  				eval(context.Background(), benchmark.parameter)
   141  			}
   142  		})
   143  		bench.Run(benchmark.name+"_parsing", func(bench *testing.B) {
   144  			for i := 0; i < bench.N; i++ {
   145  				Full().NewEvaluable(benchmark.expression)
   146  			}
   147  		})
   148  
   149  	}
   150  }
   151  

View as plain text