-
-
Notifications
You must be signed in to change notification settings - Fork 463
Why the best way to define a function from a performance perspective is to use the expr.Function option. #767
-
The best way to define a function from a performance perspective is to use a Function option.
Quoted from documentation(https://expr-lang.org/docs/functions)
This is my benchmark test:
package main import ( "strconv" "testing" "github.com/expr-lang/expr" ) type EnvStruct struct{} func (EnvStruct) Atoi(input string) int { value, _ := strconv.Atoi(input) return value } func BenchmarkCustomFunctions(b *testing.B) { exprString := `Atoi("42")` // Method 1: Add to environment envMap := map[string]interface{}{ "Atoi": func(input string) int { value, _ := strconv.Atoi(input) return value }, } compiledEnvMap, err := expr.Compile(exprString, expr.Env(envMap)) if err != nil { b.Fatal(err) } // Method 2: Define on struct envStruct := EnvStruct{} compiledEnvStruct, err := expr.Compile(exprString, expr.Env(envStruct)) if err != nil { b.Fatal(err) } // Method 3: Using Function option atoiFunc := expr.Function( "Atoi", func(params ...interface{}) (interface{}, error) { return strconv.Atoi(params[0].(string)) }, strconv.Atoi, ) compiledFunc, err := expr.Compile(exprString, atoiFunc) if err != nil { b.Fatal(err) } b.Run("EnvMap", func(b *testing.B) { for i := 0; i < b.N; i++ { r, _ := expr.Run(compiledEnvMap, envMap) if r != 42 { b.Errorf("got %v, want 42", r) } } }) b.Run("EnvStruct", func(b *testing.B) { for i := 0; i < b.N; i++ { r, _ := expr.Run(compiledEnvStruct, envStruct) if r != 42 { b.Errorf("got %v, want 42", r) } } }) b.Run("FunctionOption", func(b *testing.B) { for i := 0; i < b.N; i++ { r, _ := expr.Run(compiledFunc, nil) if r != 42 { b.Errorf("got %v, want 42", r) } } }) }
And result
goos: darwin
goarch: arm64
cpu: Apple M1 Pro
BenchmarkCustomFunctions/EnvMap-10 24067287 48.26 ns/op
BenchmarkCustomFunctions/EnvStruct-10 3250443 365.2 ns/op
BenchmarkCustomFunctions/FunctionOption-10 19875295 60.49 ns/op
PASS
ok command-line-arguments 4.534s
goos: darwin
goarch: arm64
cpu: Apple M1 Pro
BenchmarkCustomFunctions/EnvMap-10 23904123 48.59 ns/op
BenchmarkCustomFunctions/EnvStruct-10 3274218 374.6 ns/op
BenchmarkCustomFunctions/FunctionOption-10 19192731 61.28 ns/op
PASS
ok command-line-arguments 4.460s
The performance of map and function methods are very close, there is no particularly obvious difference, and map method is even faster.
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 4 comments 1 reply
-
This is great, very helpful! I've wondered about this since I make heavy use of functions. It's nice to have data.
Beta Was this translation helpful? Give feedback.
All reactions
-
🎉 1
-
Nice benchmark!
I put a lot of effort to make function calls as fast as possible.
This is still true: The best way to define a function from a performance perspective is to use a Function option.
In your case the map slightly outperforms Function due to a heavy ammount of optimizations. But for general, uncommon func signature results will be different. True for example: func(int, string, int, time.Time, uin16) or any other random funcs signatures.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
For such a function func(int, string, int, time.Time, uin16) , will the expr.Function be much faster than the map? Or is it just a little faster?
Beta Was this translation helpful? Give feedback.
All reactions
-
Faster. But on how much - benchmarks are needed.
Beta Was this translation helpful? Give feedback.
All reactions
-
See this example as a (not self-contained) benchmark: akvorado/akvorado#1769.
Beta Was this translation helpful? Give feedback.