Skip to content

Commit

Permalink
runtime: Don't value copy structs from pointers on property access
Browse files Browse the repository at this point in the history
Adds a test with a 10mb struct passed by a pointer, it would be previously
copied on property accessed whereas now the accesses just go through the
pointer without a copy.

Previous:
Benchmark_largeStructAccess-4          200     7914350 ns/op

After:
Benchmark_largeStructAccess-4      2000000         912 ns/op
  • Loading branch information
deanm authored and antonmedv committed Aug 17, 2019
1 parent 33a808e commit 6f0c855
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 8 deletions.
22 changes: 22 additions & 0 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,25 @@ func Benchmark_call(b *testing.B) {
b.Fatal(err)
}
}

func Benchmark_largeStructAccess(b *testing.B) {
type Env struct {
Data [1024*1024*10]byte
Field int
}

program, err := expr.Compile(`Field > 0 && Field > 1 && Field < 20`, expr.Env(Env{}))
if err != nil {
b.Fatal(err)
}

env := Env{Field: 21}

for n := 0; n < b.N; n++ {
_, err = vm.Run(program, &env)
}

if err != nil {
b.Fatal(err)
}
}
18 changes: 10 additions & 8 deletions vm/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@ type Scope map[string]interface{}

func fetch(from interface{}, i interface{}) interface{} {
v := reflect.ValueOf(from)
switch v.Kind() {
kind := v.Kind()

// Structures can be access through a pointer or through a value, when they
// are accessed through a pointer we don't want to copy them to a value.
if kind == reflect.Ptr && reflect.Indirect(v).Kind() == reflect.Struct {
v = reflect.Indirect(v)
kind = v.Kind()
}

switch kind {

case reflect.Array, reflect.Slice, reflect.String:
index := toInt(i)
Expand All @@ -37,13 +46,6 @@ func fetch(from interface{}, i interface{}) interface{} {
if value.IsValid() && value.CanInterface() {
return value.Interface()
}

case reflect.Ptr:
value := v.Elem()
if value.IsValid() && value.CanInterface() {
return fetch(value.Interface(), i)
}

}
return nil
}
Expand Down

0 comments on commit 6f0c855

Please sign in to comment.