Skip to content

Commit d676309

Browse files
committed
allow to reuse vm
1 parent 637431e commit d676309

File tree

5 files changed

+132
-28
lines changed

5 files changed

+132
-28
lines changed

bench_test.go

+54
Large diffs are not rendered by default.

cmd/exe/debugger.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func debugger() {
3232
program, err := compiler.Compile(tree, nil)
3333
check(err)
3434

35-
vm := NewVM(true)
35+
vm := Debug()
3636

3737
app := tview.NewApplication()
3838
table := tview.NewTable()
@@ -54,7 +54,7 @@ func debugger() {
5454
app.SetRoot(flex, true)
5555

5656
go func() {
57-
out := vm.Run(program, nil)
57+
out, _ := vm.Run(program, nil)
5858
app.QueueUpdateDraw(func() {
5959
sub.RemoveItem(scope)
6060
result := tview.NewTextView()

docs/Optimizations.md

+37
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,41 @@ Will be replaced with result of `fib(42)` on compile step. No need to calculate
7979

8080
[ConstExpr Example](https://pkg.go.dev/github.com/antonmedv/expr?tab=doc#ConstExpr)
8181

82+
## Reuse VM
83+
84+
It is possible to reuse a virtual machine between re-runs on the program.
85+
This adds a small increase in performance (from 4% to 40% depending on a program).
86+
87+
```go
88+
package main
89+
90+
import (
91+
"fmt"
92+
"github.com/antonmedv/expr"
93+
"github.com/antonmedv/expr/vm"
94+
)
95+
96+
func main() {
97+
env := map[string]interface{}{
98+
"foo": 1,
99+
"bar": 2,
100+
}
101+
102+
program, err := expr.Compile("foo + bar", expr.Env(env))
103+
if err != nil {
104+
panic(err)
105+
}
106+
107+
// Reuse this vm instance between runs
108+
v := vm.VM{}
109+
110+
out, err := v.Run(program, env)
111+
if err != nil {
112+
panic(err)
113+
}
114+
115+
fmt.Print(out)
116+
}
117+
```
118+
82119
* [Contents](README.md)

go.sum

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
22
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
34
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
46
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
57
github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM=
68
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
@@ -11,6 +13,7 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
1113
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
1214
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
1315
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
16+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1417
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1518
github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498 h1:4CFNy7/q7P06AsIONZzuWy7jcdqEmYQvOZ9FAFZdbls=
1619
github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84=
@@ -26,7 +29,9 @@ golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7w
2629
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
2730
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2831
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
32+
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
2933
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
3034
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
3135
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
36+
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
3237
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

vm/vm.go

+34-26
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,13 @@ var (
1313
MemoryBudget int = 1e6
1414
)
1515

16-
func Run(program *Program, env interface{}) (out interface{}, err error) {
16+
func Run(program *Program, env interface{}) (interface{}, error) {
1717
if program == nil {
1818
return nil, fmt.Errorf("program is nil")
1919
}
2020

21-
vm := NewVM(false)
22-
23-
defer func() {
24-
if r := recover(); r != nil {
25-
f := &file.Error{
26-
Location: program.Locations[vm.pp],
27-
Message: fmt.Sprintf("%v", r),
28-
}
29-
err = f.Bind(program.Source)
30-
}
31-
}()
32-
33-
out = vm.Run(program, env)
34-
return
21+
vm := VM{}
22+
return vm.Run(program, env)
3523
}
3624

3725
type VM struct {
@@ -48,20 +36,40 @@ type VM struct {
4836
limit int
4937
}
5038

51-
func NewVM(debug bool) *VM {
39+
func Debug() *VM {
5240
vm := &VM{
53-
stack: make([]interface{}, 0, 2),
54-
debug: debug,
55-
limit: MemoryBudget,
56-
}
57-
if vm.debug {
58-
vm.step = make(chan struct{}, 0)
59-
vm.curr = make(chan int, 0)
41+
debug: true,
42+
step: make(chan struct{}, 0),
43+
curr: make(chan int, 0),
6044
}
6145
return vm
6246
}
6347

64-
func (vm *VM) Run(program *Program, env interface{}) interface{} {
48+
func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error) {
49+
defer func() {
50+
if r := recover(); r != nil {
51+
f := &file.Error{
52+
Location: program.Locations[vm.pp],
53+
Message: fmt.Sprintf("%v", r),
54+
}
55+
err = f.Bind(program.Source)
56+
}
57+
}()
58+
59+
vm.limit = MemoryBudget
60+
vm.ip = 0
61+
vm.pp = 0
62+
63+
if vm.stack == nil {
64+
vm.stack = make([]interface{}, 0, 2)
65+
} else {
66+
vm.stack = vm.stack[0:0]
67+
}
68+
69+
if vm.scopes != nil {
70+
vm.scopes = vm.scopes[0:0]
71+
}
72+
6573
vm.bytecode = program.Bytecode
6674
vm.constants = program.Constants
6775

@@ -379,10 +387,10 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
379387
}
380388

381389
if len(vm.stack) > 0 {
382-
return vm.pop()
390+
return vm.pop(), nil
383391
}
384392

385-
return nil
393+
return nil, nil
386394
}
387395

388396
func (vm *VM) push(value interface{}) {

0 commit comments

Comments
 (0)