Comments (11)
Hmm, I think this is due to cached regex.
from expr.
hi, i'am interested in using this library in my project, but it requires to store the expression into database.
i've created some benchmark regarding the marshalling process with comparison to govaluate performace and found that Unmarshaling from json takes as much time as re-compiling the script (though it allocated less bytes). Shouldn't vm.Program implement encoding.TextMarshaler instead and use faster serialisation method? for example using go codec for binary representation + base64 for string representation
import (
"encoding/base64"
"encoding/json"
"testing"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/ugorji/go/codec"
"gopkg.in/Knetic/govaluate.v3"
)
func BenchmarkLibrary(b *testing.B) {
parameters := map[string]interface{}{
"s_age": 10,
}
s := `s_age == 10 || s_age < 10`
b.Run("expr", func(b *testing.B) {
for n := 0; n < b.N; n++ {
p, _ := expr.Compile(s, expr.Env(parameters))
expr.Run(p, parameters)
}
})
b.Run("expr-marshalled", func(b *testing.B) {
p, _ := expr.Compile(s, expr.Env(parameters))
j, _ := json.Marshal(p)
for n := 0; n < b.N; n++ {
uP := &vm.Program{}
json.Unmarshal(j, &uP)
expr.Run(uP, parameters)
}
})
b.Run("expr-go-codec", func(b *testing.B) {
p, _ := expr.Compile(s, expr.Env(parameters))
var bh codec.BincHandle
var data []byte
enc := codec.NewEncoderBytes(&data, &bh)
enc.Encode(p)
s := base64.StdEncoding.EncodeToString(data)
for n := 0; n < b.N; n++ {
data2, _ := base64.StdEncoding.DecodeString(s)
p2 := &vm.Program{}
dec := codec.NewDecoderBytes(data2, &bh)
dec.Decode(p2)
expr.Run(p2, parameters)
}
})
b.Run("govaluate", func(b *testing.B) {
for n := 0; n < b.N; n++ {
expression, _ := govaluate.NewEvaluableExpression(s)
expression.Evaluate(parameters)
}
})
}
BenchmarkLibrary/expr-8 50000 27376 ns/op 10600 B/op 222 allocs/op
BenchmarkLibrary/expr-marshalled-8 50000 26022 ns/op 3097 B/op 97 allocs/op
BenchmarkLibrary/expr-go-codec-8 200000 7798 ns/op 2160 B/op 23 allocs/op
BenchmarkLibrary/govaluate-8 300000 4317 ns/op 2136 B/op 52 allocs/op
from expr.
Hi,
If you want to store a compiled program in DB why do you benchmark compiling and unmarshaling at the same time?
+ p, _ := expr.Compile(s, expr.Env(parameters))
+ j, _ := json.Marshal(p)
b.Run("expr-marshalled", func(b *testing.B) {
- p, _ := expr.Compile(s, expr.Env(parameters))
- j, _ := json.Marshal(p)
for n := 0; n < b.N; n++ {
uP := &vm.Program{}
json.Unmarshal(j, &uP)
expr.Run(uP, parameters)
}
})
But this is really interesting. I think it's will be cool to implement fast marshal/unmarshal for a program. In my use case, we keep the program compiled in memory, so I never taked a look at speed on unmarshaling.
Currently, a program consists of these things:
type Program struct {
Source *file.Source // Original source code
Locations []file.Location // References of bytecodes to source code location (used for showing code snippets on error)
Constants []interface{} // List of constants
Bytecode []byte // Bytecode of a program itself.
}
Maybe you can create a PR with improvements?
from expr.
If you want to store a compiled program in DB why do you benchmark compiling and unmarshaling at the same time
Oops, my mistake, should have taken that out from run(). But i think it won't change the overall result .
I can create a PR, but i'm not confident with what binary encoding would be best. It also hasn't solve the regex problem too.
from expr.
Will be cool first, understand which part of a program slow to unmarshal (Sorce, Locations, Constants, Bytecode) and may be using encoding/gob
for Constants
will help?
from expr.
Depends on what will be stored in Constant
(currently i haven't dig deeper on the source code). AFAIK encoding/gob
will return error when a struct has no exported field.
from expr.
Constant
usually it is int
, string
, vm.Call
and some maps/slices.
from expr.
And the maps/slice are also int
, string
, and vm.Call
?
Just out of curiousity, is Constant
related to the cached regex you've mention before?
Btw, What about Source
?, this is the one that failed applying encoding/gob
to vm.Program
as a whole
from expr.
Yes, regex also stored in Constants
(as we don't want to recompile regexp on each execution of a program). Usually map/slice
stored in Constants
are from expressions. For example, if you compile
foo in [1, 2, 3]
Constants
will contain []int
slice, so we don't need to recalculate it on program execution.
Source is just a string of source code with some chaced offest indexes. Fields can be made public to mage it available for gob.
from expr.
after adding encoding.BinaryMarshaller
and encoding.BinaryUnmarshaller
to Source
, encoding with gob now success. But when you add regex, gob also fail
func TestProgram_Marshall(t *testing.T) {
param := map[string]interface{}{
"subject": map[string]interface{}{
"age": 10,
},
}
s := `subject.age > 10 && "hello" matches "hell"`
p, err := Compile(s, Env(param))
if err != nil {
t.Error(err)
}
var buff bytes.Buffer
enc := gob.NewEncoder(&buff)
dec := gob.NewDecoder(&buff)
if err := enc.Encode(p); err != nil {
t.Error("encode error: ", err)
}
p2 := &vm.Program{}
if err := dec.Decode(p2); err != nil {
t.Error("decode error: ", err)
}
o, err := Run(p2, param)
if err != nil {
t.Error(err)
}
if v, ok := o.(bool); !ok || !v {
t.Error("should match")
}
}
encode error: gob: type not registered for interface: regexp.Regexp
I think expr need to wrap regexp and implement encoding.BinaryMarshaller
and encoding.BinaryUnmarshaller
and use the String()
value of regexp.Regexp
from expr.
Marshaling is not considered impossible in general case. As ConstExpr results also saved in constants.
from expr.
Related Issues (20)
- Interface conversion error with new version HOT 2
- OSS-Fuzz issue 61913
- Change in behavior in 1.15 with `in` and float64 HOT 4
- OSS-Fuzz issue 61959
- OSS-Fuzz issue 61962 HOT 1
- Compile less OpDeref for any type
- Cache compiled code to file HOT 2
- Propagate `uint32` arguments types in ast HOT 7
- "Not Equal to Nil" kind of check before executing expressions HOT 9
- Nil safety
- OSS-Fuzz issue 62148 HOT 1
- OSS-Fuzz issue 62254 HOT 2
- Function name limitation HOT 1
- Coalesce operator missing parentheses after ast print HOT 2
- Questions about the performance. HOT 1
- picking the value of a field from a filtered array HOT 1
- Using the type statement to create a new int type, expr is unable to compare the new type with int.
- sort doesn't work on floats
- Playground error on unsupported type: map[interface {}]interface {} HOT 1
- Porting to javascript
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from expr.