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 <zhr...@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: 望哥 <gelny...@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
        }

Reply via email to