github.com/expr-lang/expr@v1.16.9/bench_test.go (about)

     1  package expr_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/expr-lang/expr/internal/testify/require"
     7  
     8  	"github.com/expr-lang/expr"
     9  	"github.com/expr-lang/expr/vm"
    10  )
    11  
    12  func Benchmark_expr(b *testing.B) {
    13  	params := make(map[string]any)
    14  	params["Origin"] = "MOW"
    15  	params["Country"] = "RU"
    16  	params["Adults"] = 1
    17  	params["Value"] = 100
    18  
    19  	program, err := expr.Compile(`(Origin == "MOW" || Country == "RU") && (Value >= 100 || Adults == 1)`, expr.Env(params))
    20  	require.NoError(b, err)
    21  
    22  	var out any
    23  
    24  	b.ResetTimer()
    25  	for n := 0; n < b.N; n++ {
    26  		out, err = vm.Run(program, params)
    27  	}
    28  	b.StopTimer()
    29  
    30  	require.NoError(b, err)
    31  	require.True(b, out.(bool))
    32  }
    33  
    34  func Benchmark_expr_reuseVm(b *testing.B) {
    35  	params := make(map[string]any)
    36  	params["Origin"] = "MOW"
    37  	params["Country"] = "RU"
    38  	params["Adults"] = 1
    39  	params["Value"] = 100
    40  
    41  	program, err := expr.Compile(`(Origin == "MOW" || Country == "RU") && (Value >= 100 || Adults == 1)`, expr.Env(params))
    42  	require.NoError(b, err)
    43  
    44  	var out any
    45  	v := vm.VM{}
    46  
    47  	b.ResetTimer()
    48  	for n := 0; n < b.N; n++ {
    49  		out, err = v.Run(program, params)
    50  	}
    51  	b.StopTimer()
    52  
    53  	require.NoError(b, err)
    54  	require.True(b, out.(bool))
    55  }
    56  
    57  func Benchmark_len(b *testing.B) {
    58  	env := map[string]any{
    59  		"arr": make([]int, 100),
    60  	}
    61  
    62  	program, err := expr.Compile(`len(arr)`, expr.Env(env))
    63  	require.NoError(b, err)
    64  
    65  	var out any
    66  	b.ResetTimer()
    67  	for n := 0; n < b.N; n++ {
    68  		out, err = vm.Run(program, env)
    69  	}
    70  	b.StopTimer()
    71  
    72  	require.NoError(b, err)
    73  	require.Equal(b, 100, out)
    74  }
    75  
    76  func Benchmark_filter(b *testing.B) {
    77  	type Env struct {
    78  		Ints []int
    79  	}
    80  	env := Env{
    81  		Ints: make([]int, 1000),
    82  	}
    83  	for i := 1; i <= len(env.Ints); i++ {
    84  		env.Ints[i-1] = i
    85  	}
    86  
    87  	program, err := expr.Compile(`filter(Ints, # % 7 == 0)`, expr.Env(Env{}))
    88  	require.NoError(b, err)
    89  
    90  	var out any
    91  	b.ResetTimer()
    92  	for n := 0; n < b.N; n++ {
    93  		out, err = vm.Run(program, env)
    94  	}
    95  	b.StopTimer()
    96  
    97  	require.NoError(b, err)
    98  	require.Len(b, out.([]any), 142)
    99  }
   100  
   101  func Benchmark_filterLen(b *testing.B) {
   102  	type Env struct {
   103  		Ints []int
   104  	}
   105  	env := Env{
   106  		Ints: make([]int, 1000),
   107  	}
   108  	for i := 1; i <= len(env.Ints); i++ {
   109  		env.Ints[i-1] = i
   110  	}
   111  
   112  	program, err := expr.Compile(`len(filter(Ints, # % 7 == 0))`, expr.Env(Env{}))
   113  	require.NoError(b, err)
   114  
   115  	var out any
   116  	b.ResetTimer()
   117  	for n := 0; n < b.N; n++ {
   118  		out, err = vm.Run(program, env)
   119  	}
   120  	b.StopTimer()
   121  
   122  	require.NoError(b, err)
   123  	require.Equal(b, 142, out)
   124  }
   125  
   126  func Benchmark_filterFirst(b *testing.B) {
   127  	type Env struct {
   128  		Ints []int
   129  	}
   130  	env := Env{
   131  		Ints: make([]int, 1000),
   132  	}
   133  	for i := 1; i <= len(env.Ints); i++ {
   134  		env.Ints[i-1] = i
   135  	}
   136  
   137  	program, err := expr.Compile(`filter(Ints, # % 7 == 0)[0]`, expr.Env(Env{}))
   138  	require.NoError(b, err)
   139  
   140  	var out any
   141  	b.ResetTimer()
   142  	for n := 0; n < b.N; n++ {
   143  		out, err = vm.Run(program, env)
   144  	}
   145  	b.StopTimer()
   146  
   147  	require.NoError(b, err)
   148  	require.Equal(b, 7, out)
   149  }
   150  
   151  func Benchmark_filterLast(b *testing.B) {
   152  	type Env struct {
   153  		Ints []int
   154  	}
   155  	env := Env{
   156  		Ints: make([]int, 1000),
   157  	}
   158  	for i := 1; i <= len(env.Ints); i++ {
   159  		env.Ints[i-1] = i
   160  	}
   161  
   162  	program, err := expr.Compile(`filter(Ints, # % 7 == 0)[-1]`, expr.Env(Env{}))
   163  	require.NoError(b, err)
   164  
   165  	var out any
   166  	b.ResetTimer()
   167  	for n := 0; n < b.N; n++ {
   168  		out, err = vm.Run(program, env)
   169  	}
   170  
   171  	b.StopTimer()
   172  
   173  	require.NoError(b, err)
   174  	require.Equal(b, 994, out)
   175  }
   176  
   177  func Benchmark_filterMap(b *testing.B) {
   178  	type Env struct {
   179  		Ints []int
   180  	}
   181  	env := Env{
   182  		Ints: make([]int, 100),
   183  	}
   184  	for i := 1; i <= len(env.Ints); i++ {
   185  		env.Ints[i-1] = i
   186  	}
   187  
   188  	program, err := expr.Compile(`map(filter(Ints, # % 7 == 0), # * 2)`, expr.Env(Env{}))
   189  	require.NoError(b, err)
   190  
   191  	var out any
   192  	b.ResetTimer()
   193  	for n := 0; n < b.N; n++ {
   194  		out, err = vm.Run(program, env)
   195  	}
   196  	b.StopTimer()
   197  
   198  	require.NoError(b, err)
   199  	require.Len(b, out.([]any), 14)
   200  	require.Equal(b, 14, out.([]any)[0])
   201  }
   202  
   203  func Benchmark_arrayIndex(b *testing.B) {
   204  	env := map[string]any{
   205  		"arr": make([]int, 100),
   206  	}
   207  	for i := 0; i < 100; i++ {
   208  		env["arr"].([]int)[i] = i
   209  	}
   210  
   211  	program, err := expr.Compile(`arr[50]`, expr.Env(env))
   212  	require.NoError(b, err)
   213  
   214  	var out any
   215  	b.ResetTimer()
   216  	for n := 0; n < b.N; n++ {
   217  		out, err = vm.Run(program, env)
   218  	}
   219  	b.StopTimer()
   220  
   221  	require.NoError(b, err)
   222  	require.Equal(b, 50, out)
   223  }
   224  
   225  func Benchmark_envStruct(b *testing.B) {
   226  	type Price struct {
   227  		Value int
   228  	}
   229  	type Env struct {
   230  		Price Price
   231  	}
   232  
   233  	program, err := expr.Compile(`Price.Value > 0`, expr.Env(Env{}))
   234  	require.NoError(b, err)
   235  
   236  	env := Env{Price: Price{Value: 1}}
   237  
   238  	var out any
   239  	b.ResetTimer()
   240  	for n := 0; n < b.N; n++ {
   241  		out, err = vm.Run(program, env)
   242  	}
   243  	b.StopTimer()
   244  
   245  	require.NoError(b, err)
   246  	require.True(b, out.(bool))
   247  }
   248  
   249  func Benchmark_envMap(b *testing.B) {
   250  	type Price struct {
   251  		Value int
   252  	}
   253  	env := map[string]any{
   254  		"price": Price{Value: 1},
   255  	}
   256  
   257  	program, err := expr.Compile(`price.Value > 0`, expr.Env(env))
   258  	require.NoError(b, err)
   259  
   260  	var out any
   261  	b.ResetTimer()
   262  	for n := 0; n < b.N; n++ {
   263  		out, err = vm.Run(program, env)
   264  	}
   265  	b.StopTimer()
   266  
   267  	require.NoError(b, err)
   268  	require.True(b, out.(bool))
   269  }
   270  
   271  type CallEnv struct {
   272  	A      int
   273  	B      int
   274  	C      int
   275  	Fn     func() bool
   276  	FnFast func(...any) any
   277  	Foo    CallFoo
   278  }
   279  
   280  func (CallEnv) Func() string {
   281  	return "func"
   282  }
   283  
   284  type CallFoo struct {
   285  	D int
   286  	E int
   287  	F int
   288  }
   289  
   290  func (CallFoo) Method() string {
   291  	return "method"
   292  }
   293  
   294  func Benchmark_callFunc(b *testing.B) {
   295  	program, err := expr.Compile(`Func()`, expr.Env(CallEnv{}))
   296  	require.NoError(b, err)
   297  
   298  	env := CallEnv{}
   299  
   300  	var out any
   301  	b.ResetTimer()
   302  	for n := 0; n < b.N; n++ {
   303  		out, err = vm.Run(program, env)
   304  	}
   305  	b.StopTimer()
   306  
   307  	require.NoError(b, err)
   308  	require.Equal(b, "func", out)
   309  }
   310  
   311  func Benchmark_callMethod(b *testing.B) {
   312  	program, err := expr.Compile(`Foo.Method()`, expr.Env(CallEnv{}))
   313  	require.NoError(b, err)
   314  
   315  	env := CallEnv{}
   316  
   317  	var out any
   318  	b.ResetTimer()
   319  	for n := 0; n < b.N; n++ {
   320  		out, err = vm.Run(program, env)
   321  	}
   322  	b.StopTimer()
   323  
   324  	require.NoError(b, err)
   325  	require.Equal(b, "method", out)
   326  }
   327  
   328  func Benchmark_callField(b *testing.B) {
   329  	program, err := expr.Compile(`Fn()`, expr.Env(CallEnv{}))
   330  	require.NoError(b, err)
   331  
   332  	env := CallEnv{
   333  		Fn: func() bool {
   334  			return true
   335  		},
   336  	}
   337  
   338  	var out any
   339  	b.ResetTimer()
   340  	for n := 0; n < b.N; n++ {
   341  		out, err = vm.Run(program, env)
   342  	}
   343  	b.StopTimer()
   344  
   345  	require.NoError(b, err)
   346  	require.True(b, out.(bool))
   347  }
   348  
   349  func Benchmark_callFast(b *testing.B) {
   350  	program, err := expr.Compile(`FnFast()`, expr.Env(CallEnv{}))
   351  	if err != nil {
   352  		b.Fatal(err)
   353  	}
   354  
   355  	env := CallEnv{
   356  		FnFast: func(s ...any) any {
   357  			return "fn_fast"
   358  		},
   359  	}
   360  
   361  	var out any
   362  	b.ResetTimer()
   363  	for n := 0; n < b.N; n++ {
   364  		out, err = vm.Run(program, env)
   365  	}
   366  	b.StopTimer()
   367  
   368  	require.NoError(b, err)
   369  	require.Equal(b, "fn_fast", out)
   370  }
   371  
   372  func Benchmark_callConstExpr(b *testing.B) {
   373  	program, err := expr.Compile(`Func()`, expr.Env(CallEnv{}), expr.ConstExpr("Func"))
   374  	require.NoError(b, err)
   375  
   376  	env := CallEnv{}
   377  
   378  	var out any
   379  	b.ResetTimer()
   380  	for n := 0; n < b.N; n++ {
   381  		out, err = vm.Run(program, env)
   382  	}
   383  	b.StopTimer()
   384  
   385  	require.NoError(b, err)
   386  	require.Equal(b, "func", out)
   387  }
   388  
   389  func Benchmark_largeStructAccess(b *testing.B) {
   390  	type Env struct {
   391  		Data  [1024 * 1024 * 10]byte
   392  		Field int
   393  	}
   394  
   395  	program, err := expr.Compile(`Field > 0 && Field > 1 && Field < 99`, expr.Env(Env{}))
   396  	require.NoError(b, err)
   397  
   398  	env := Env{Field: 21}
   399  
   400  	var out any
   401  	b.ResetTimer()
   402  	for n := 0; n < b.N; n++ {
   403  		out, err = vm.Run(program, &env)
   404  	}
   405  	b.StopTimer()
   406  
   407  	require.NoError(b, err)
   408  	require.True(b, out.(bool))
   409  }
   410  
   411  func Benchmark_largeNestedStructAccess(b *testing.B) {
   412  	type Env struct {
   413  		Inner struct {
   414  			Data  [1024 * 1024 * 10]byte
   415  			Field int
   416  		}
   417  	}
   418  
   419  	program, err := expr.Compile(`Inner.Field > 0 && Inner.Field > 1 && Inner.Field < 99`, expr.Env(Env{}))
   420  	require.NoError(b, err)
   421  
   422  	env := Env{}
   423  	env.Inner.Field = 21
   424  
   425  	var out any
   426  	b.ResetTimer()
   427  	for n := 0; n < b.N; n++ {
   428  		out, err = vm.Run(program, &env)
   429  	}
   430  	b.StopTimer()
   431  
   432  	require.NoError(b, err)
   433  	require.True(b, out.(bool))
   434  }
   435  
   436  func Benchmark_largeNestedArrayAccess(b *testing.B) {
   437  	type Env struct {
   438  		Data [1][1024 * 1024 * 10]byte
   439  	}
   440  
   441  	program, err := expr.Compile(`Data[0][0] > 0`, expr.Env(Env{}))
   442  	require.NoError(b, err)
   443  
   444  	env := Env{}
   445  	env.Data[0][0] = 1
   446  
   447  	var out any
   448  	b.ResetTimer()
   449  	for n := 0; n < b.N; n++ {
   450  		out, err = vm.Run(program, &env)
   451  	}
   452  	b.StopTimer()
   453  
   454  	require.NoError(b, err)
   455  	require.True(b, out.(bool))
   456  }
   457  
   458  func Benchmark_sort(b *testing.B) {
   459  	env := map[string]any{
   460  		"arr": []any{55, 58, 42, 61, 75, 52, 64, 62, 16, 79, 40, 14, 50, 76, 23, 2, 5, 80, 89, 51, 21, 96, 91, 13, 71, 82, 65, 63, 11, 17, 94, 81, 74, 4, 97, 1, 39, 3, 28, 8, 84, 90, 47, 85, 7, 56, 49, 93, 33, 12, 19, 60, 86, 100, 44, 45, 36, 72, 95, 77, 34, 92, 24, 73, 18, 38, 43, 26, 41, 69, 67, 57, 9, 27, 66, 87, 46, 35, 59, 70, 10, 20, 53, 15, 32, 98, 68, 31, 54, 25, 83, 88, 22, 48, 29, 37, 6, 78, 99, 30},
   461  	}
   462  
   463  	program, err := expr.Compile(`sort(arr)`, expr.Env(env))
   464  	require.NoError(b, err)
   465  
   466  	var out any
   467  	b.ResetTimer()
   468  	for n := 0; n < b.N; n++ {
   469  		out, _ = vm.Run(program, env)
   470  	}
   471  	b.StopTimer()
   472  
   473  	require.Equal(b, 1, out.([]any)[0])
   474  	require.Equal(b, 100, out.([]any)[99])
   475  }
   476  
   477  func Benchmark_sortBy(b *testing.B) {
   478  	type Foo struct {
   479  		Value int
   480  	}
   481  	arr := []any{55, 58, 42, 61, 75, 52, 64, 62, 16, 79, 40, 14, 50, 76, 23, 2, 5, 80, 89, 51, 21, 96, 91, 13, 71, 82, 65, 63, 11, 17, 94, 81, 74, 4, 97, 1, 39, 3, 28, 8, 84, 90, 47, 85, 7, 56, 49, 93, 33, 12, 19, 60, 86, 100, 44, 45, 36, 72, 95, 77, 34, 92, 24, 73, 18, 38, 43, 26, 41, 69, 67, 57, 9, 27, 66, 87, 46, 35, 59, 70, 10, 20, 53, 15, 32, 98, 68, 31, 54, 25, 83, 88, 22, 48, 29, 37, 6, 78, 99, 30}
   482  	env := map[string]any{
   483  		"arr": make([]Foo, len(arr)),
   484  	}
   485  	for i, v := range arr {
   486  		env["arr"].([]Foo)[i] = Foo{Value: v.(int)}
   487  	}
   488  
   489  	program, err := expr.Compile(`sortBy(arr, .Value)`, expr.Env(env))
   490  	require.NoError(b, err)
   491  
   492  	var out any
   493  	b.ResetTimer()
   494  	for n := 0; n < b.N; n++ {
   495  		out, _ = vm.Run(program, env)
   496  	}
   497  	b.StopTimer()
   498  
   499  	require.Equal(b, 1, out.([]any)[0].(Foo).Value)
   500  	require.Equal(b, 100, out.([]any)[99].(Foo).Value)
   501  }
   502  
   503  func Benchmark_groupBy(b *testing.B) {
   504  	program, err := expr.Compile(`groupBy(1..100, # % 7)[6]`)
   505  	require.NoError(b, err)
   506  
   507  	var out any
   508  	b.ResetTimer()
   509  	for n := 0; n < b.N; n++ {
   510  		out, _ = vm.Run(program, nil)
   511  	}
   512  	b.StopTimer()
   513  
   514  	require.Equal(b, 6, out.([]any)[0])
   515  }
   516  
   517  func Benchmark_reduce(b *testing.B) {
   518  	program, err := expr.Compile(`reduce(1..100, # + #acc)`)
   519  	require.NoError(b, err)
   520  
   521  	var out any
   522  	b.ResetTimer()
   523  	for n := 0; n < b.N; n++ {
   524  		out, _ = vm.Run(program, nil)
   525  	}
   526  	b.StopTimer()
   527  
   528  	require.Equal(b, 5050, out.(int))
   529  }