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 }