Skip to content

Commit 6f0c855

Browse files
deanmantonmedv
authored andcommitted
runtime: Don't value copy structs from pointers on property access
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
1 parent 33a808e commit 6f0c855

File tree

2 files changed

+32
-8
lines changed

2 files changed

+32
-8
lines changed

bench_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,25 @@ func Benchmark_call(b *testing.B) {
9797
b.Fatal(err)
9898
}
9999
}
100+
101+
func Benchmark_largeStructAccess(b *testing.B) {
102+
type Env struct {
103+
Data [1024*1024*10]byte
104+
Field int
105+
}
106+
107+
program, err := expr.Compile(`Field > 0 && Field > 1 && Field < 20`, expr.Env(Env{}))
108+
if err != nil {
109+
b.Fatal(err)
110+
}
111+
112+
env := Env{Field: 21}
113+
114+
for n := 0; n < b.N; n++ {
115+
_, err = vm.Run(program, &env)
116+
}
117+
118+
if err != nil {
119+
b.Fatal(err)
120+
}
121+
}

vm/runtime.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,16 @@ type Scope map[string]interface{}
1717

1818
func fetch(from interface{}, i interface{}) interface{} {
1919
v := reflect.ValueOf(from)
20-
switch v.Kind() {
20+
kind := v.Kind()
21+
22+
// Structures can be access through a pointer or through a value, when they
23+
// are accessed through a pointer we don't want to copy them to a value.
24+
if kind == reflect.Ptr && reflect.Indirect(v).Kind() == reflect.Struct {
25+
v = reflect.Indirect(v)
26+
kind = v.Kind()
27+
}
28+
29+
switch kind {
2130

2231
case reflect.Array, reflect.Slice, reflect.String:
2332
index := toInt(i)
@@ -37,13 +46,6 @@ func fetch(from interface{}, i interface{}) interface{} {
3746
if value.IsValid() && value.CanInterface() {
3847
return value.Interface()
3948
}
40-
41-
case reflect.Ptr:
42-
value := v.Elem()
43-
if value.IsValid() && value.CanInterface() {
44-
return fetch(value.Interface(), i)
45-
}
46-
4749
}
4850
return nil
4951
}

0 commit comments

Comments
 (0)