You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by wo...@apache.org on 2020/04/20 12:54:55 UTC
[dubbo-go-hessian2] branch master updated: Imp: cache in reflection
(#179)
This is an automated email from the ASF dual-hosted git repository.
wongoo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-go-hessian2.git
The following commit(s) were added to refs/heads/master by this push:
new bb036e6 Imp: cache in reflection (#179)
bb036e6 is described below
commit bb036e6fcafdb3d897fa47615a253f716c8b0d24
Author: huiren <zh...@gmail.com>
AuthorDate: Mon Apr 20 20:54:46 2020 +0800
Imp: cache in reflection (#179)
* benchmark result
* use cache in findField
* encode benchmark
* call field() once
* remove version
* fix import sync
* cache in registerPOJO
* add json bench result
* prune unneccessary rtype.Field(index)
* cache comment
* rename cache
* switch to if
* remove return value name
* findFieldWithCache
* remove if check when fieldStruct is nil
Co-authored-by: 望哥 <ge...@163.com>
---
object.go | 82 ++++++++++++++++++++++++++++++++++++++--------------
object_test.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
pojo.go | 5 ++++
3 files changed, 155 insertions(+), 22 deletions(-)
diff --git a/object.go b/object.go
index 4025dac..d629933 100644
--- a/object.go
+++ b/object.go
@@ -21,6 +21,7 @@ import (
"io"
"reflect"
"strings"
+ "sync"
)
import (
@@ -164,21 +165,23 @@ func (e *Encoder) encObject(v POJO) error {
structs := []reflect.Value{vv}
for len(structs) > 0 {
vv := structs[0]
+ vvt := vv.Type()
num = vv.NumField()
for i = 0; i < num; i++ {
+ tf := vvt.Field(i)
// skip unexported anonymous field
- if vv.Type().Field(i).PkgPath != "" {
+ if tf.PkgPath != "" {
continue
}
// skip ignored field
- if tag, _ := vv.Type().Field(i).Tag.Lookup(tagIdentifier); tag == `-` {
+ if tag, _ := tf.Tag.Lookup(tagIdentifier); tag == `-` {
continue
}
field := vv.Field(i)
- if vv.Type().Field(i).Anonymous && field.Kind() == reflect.Struct {
- structs = append(structs, vv.Field(i))
+ if tf.Anonymous && field.Kind() == reflect.Struct {
+ structs = append(structs, field)
continue
}
@@ -292,36 +295,71 @@ func (d *Decoder) decClassDef() (interface{}, error) {
return classInfo{javaName: clsName, fieldNameList: fieldList}, nil
}
-func findField(name string, typ reflect.Type) ([]int, error) {
+type fieldInfo struct {
+ indexes []int
+ field *reflect.StructField
+}
+
+// map[rType][fieldName]indexes
+var fieldIndexCache sync.Map
+
+func findFieldWithCache(name string, typ reflect.Type) ([]int, *reflect.StructField, error) {
+ typCache, _ := fieldIndexCache.Load(typ)
+ if typCache == nil {
+ typCache = &sync.Map{}
+ fieldIndexCache.Store(typ, typCache)
+ }
+
+ iindexes, existCache := typCache.(*sync.Map).Load(name)
+ if existCache && iindexes != nil {
+ finfo := iindexes.(*fieldInfo)
+ var err error
+ if len(finfo.indexes) == 0 {
+ err = perrors.Errorf("failed to find field %s", name)
+ }
+ return finfo.indexes, finfo.field, err
+ }
+
+ indexes, field, err := findField(name, typ)
+ typCache.(*sync.Map).Store(name, &fieldInfo{indexes: indexes, field: field})
+ return indexes, field, err
+}
+
+// findField find structField in rType
+//
+// return
+// indexes []int
+// field reflect.StructField
+// err error
+func findField(name string, typ reflect.Type) ([]int, *reflect.StructField, error) {
for i := 0; i < typ.NumField(); i++ {
// matching tag first, then lowerCamelCase, SameCase, lowerCase
typField := typ.Field(i)
- if val, has := typField.Tag.Lookup(tagIdentifier); has && strings.Compare(val, name) == 0 {
- return []int{i}, nil
- }
+ tagVal, hasTag := typField.Tag.Lookup(tagIdentifier)
fieldName := typField.Name
- switch {
- case strings.Compare(lowerCamelCase(fieldName), name) == 0:
- return []int{i}, nil
- case strings.Compare(fieldName, name) == 0:
- return []int{i}, nil
- case strings.Compare(strings.ToLower(fieldName), name) == 0:
- return []int{i}, nil
+ if hasTag && tagVal == name ||
+ fieldName == name ||
+ lowerCamelCase(fieldName) == name ||
+ strings.ToLower(fieldName) == name {
+
+ return []int{i}, &typField, nil
}
if typField.Anonymous && typField.Type.Kind() == reflect.Struct {
- next, _ := findField(name, typField.Type)
+ next, field, _ := findField(name, typField.Type)
if len(next) > 0 {
- pos := []int{i}
- return append(pos, next...), nil
+ indexes := []int{i}
+ indexes = append(indexes, next...)
+
+ return indexes, field, nil
}
}
}
- return []int{}, perrors.Errorf("failed to find field %s", name)
+ return []int{}, nil, perrors.Errorf("failed to find field %s", name)
}
func (d *Decoder) decInstance(typ reflect.Type, cls classInfo) (interface{}, error) {
@@ -337,13 +375,13 @@ func (d *Decoder) decInstance(typ reflect.Type, cls classInfo) (interface{}, err
for i := 0; i < len(cls.fieldNameList); i++ {
fieldName := cls.fieldNameList[i]
- index, err := findField(fieldName, typ)
+ index, fieldStruct, err := findFieldWithCache(fieldName, typ)
if err != nil {
return nil, perrors.Errorf("can not find field %s", fieldName)
}
// skip unexported anonymous field
- if vv.Type().FieldByIndex(index).PkgPath != "" {
+ if fieldStruct.PkgPath != "" {
continue
}
@@ -483,7 +521,7 @@ func (d *Decoder) decInstance(typ reflect.Type, cls classInfo) (interface{}, err
}
default:
- return nil, perrors.Errorf("unknown struct member type: %v %v", kind, typ.Name()+"."+typ.FieldByIndex(index).Name)
+ return nil, perrors.Errorf("unknown struct member type: %v %v", kind, typ.Name()+"."+fieldStruct.Name)
}
} // end for
diff --git a/object_test.go b/object_test.go
index 2484d07..76654c7 100644
--- a/object_test.go
+++ b/object_test.go
@@ -18,9 +18,11 @@
package hessian
import (
+ "encoding/json"
"math"
"reflect"
"testing"
+ "time"
)
type Department struct {
@@ -659,3 +661,91 @@ func TestIssue150_EmbedStructJavaDecode(t *testing.T) {
testJavaDecode(t, "customArgTypedFixed_Extends", dog)
}
+
+type Mix struct {
+ A int
+ B string
+ CA time.Time
+ CB int64
+ CC string
+ CD []float64
+ D map[string]interface{}
+}
+
+func (m Mix) JavaClassName() string {
+ return `test.mix`
+}
+
+func init() {
+ RegisterPOJO(new(Mix))
+}
+
+//
+// BenchmarkJsonEncode-8 217354 4799 ns/op 832 B/op 15 allocs/op
+func BenchmarkJsonEncode(b *testing.B) {
+ m := Mix{A: int('a'), B: `hello`}
+ m.CD = []float64{1, 2, 3}
+ m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+
+ for i := 0; i < b.N; i++ {
+ _, err := json.Marshal(&m)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+}
+
+//
+// BenchmarkEncode-8 211452 5560 ns/op 1771 B/op 51 allocs/op
+func BenchmarkEncode(b *testing.B) {
+ m := Mix{A: int('a'), B: `hello`}
+ m.CD = []float64{1, 2, 3}
+ m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+
+ for i := 0; i < b.N; i++ {
+ _, err := encodeTarget(&m)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+}
+
+//
+// BenchmarkJsonDecode-8 123922 8549 ns/op 1776 B/op 51 allocs/op
+func BenchmarkJsonDecode(b *testing.B) {
+ m := Mix{A: int('a'), B: `hello`}
+ m.CD = []float64{1, 2, 3}
+ m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+ bytes, err := json.Marshal(&m)
+ if err != nil {
+ b.Error(err)
+ }
+
+ for i := 0; i < b.N; i++ {
+ m := &Mix{}
+ err := json.Unmarshal(bytes, m)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+}
+
+//
+// BenchmarkDecode-8 104196 10924 ns/op 6424 B/op 98 allocs/op
+func BenchmarkDecode(b *testing.B) {
+ m := Mix{A: int('a'), B: `hello`}
+ m.CD = []float64{1, 2, 3}
+ m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
+ bytes, err := encodeTarget(&m)
+ if err != nil {
+ b.Error(err)
+ }
+
+ for i := 0; i < b.N; i++ {
+ d := NewDecoder(bytes)
+ _, err := d.Decode()
+ if err != nil {
+ b.Error(err)
+ }
+ }
+}
diff --git a/pojo.go b/pojo.go
index 0c9f9aa..be74bac 100644
--- a/pojo.go
+++ b/pojo.go
@@ -120,6 +120,11 @@ func RegisterPOJO(o POJO) int {
pojoRegistry.Lock()
defer pojoRegistry.Unlock()
+ if goName, ok := pojoRegistry.j2g[o.JavaClassName()]; ok {
+ return pojoRegistry.registry[goName].index
+ }
+
+ // JavaClassName shouldn't equal to goName
if _, ok := pojoRegistry.registry[o.JavaClassName()]; ok {
return -1
}