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
 	}