improve performance for gconv.Struct/Structs using directly reflect set

This commit is contained in:
John
2020-11-15 15:13:40 +08:00
parent a1236b5e16
commit d56eb49e41
6 changed files with 205 additions and 4 deletions

View File

@ -50,6 +50,11 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
if pointer == nil {
return gerror.New("object pointer cannot be nil")
}
if doStructByDirectReflectSet(params, pointer) {
return nil
}
defer func() {
// Catch the panic, especially the reflect operation panics.
if e := recover(); e != nil {
@ -255,6 +260,20 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
return nil
}
// doStructByDirectReflectSet do the converting directly using reflect Set.
// It returns true if success, or else false.
func doStructByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) {
v1 := reflect.ValueOf(pointer)
v2 := reflect.ValueOf(params)
if v1.Kind() == reflect.Ptr {
if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() {
elem.Set(v2)
ok = true
}
}
return ok
}
// bindVarToStructAttr sets value to struct object attribute by name.
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping ...map[string]string) (err error) {
structFieldValue := elem.FieldByName(name)

View File

@ -38,6 +38,11 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st
if pointer == nil {
return gerror.New("object pointer cannot be nil")
}
if doStructsByDirectReflectSet(params, pointer) {
return nil
}
defer func() {
// Catch the panic, especially the reflect operation panics.
if e := recover(); e != nil {
@ -104,5 +109,18 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st
}
pointerRv.Elem().Set(array)
return nil
}
// doStructsByDirectReflectSet do the converting directly using reflect Set.
// It returns true if success, or else false.
func doStructsByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) {
v1 := reflect.ValueOf(pointer)
v2 := reflect.ValueOf(params)
if v1.Kind() == reflect.Ptr {
if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() {
elem.Set(v2)
ok = true
}
}
return ok
}

View File

@ -9,6 +9,7 @@
package gconv
import (
"reflect"
"testing"
)
@ -22,7 +23,27 @@ var (
"name": "gf",
"score": 100,
}
structPointer = new(structType)
structObj = structType{
Name: "john",
Score: 60,
}
structPointer = &structType{
Name: "john",
Score: 60,
}
structPointerNil *structType
// struct slice
structSliceNil []structType
structSlice = []structType{
{Name: "john", Score: 60},
{Name: "smith", Score: 100},
}
// struct pointer slice
structPointerSliceNil []*structType
structPointerSlice = []*structType{
{Name: "john", Score: 60},
{Name: "smith", Score: 100},
}
)
func Benchmark_Struct_Basic(b *testing.B) {
@ -30,3 +51,83 @@ func Benchmark_Struct_Basic(b *testing.B) {
Struct(structMap, structPointer)
}
}
// *struct -> **struct
func Benchmark_Reflect_PPStruct_PStruct(b *testing.B) {
for i := 0; i < b.N; i++ {
v1 := reflect.ValueOf(&structPointerNil)
v2 := reflect.ValueOf(structPointer)
//if v1.Kind() == reflect.Ptr {
// if elem := v1.Elem(); elem.Type() == v2.Type() {
// elem.Set(v2)
// }
//}
v1.Elem().Set(v2)
}
}
func Benchmark_Struct_PPStruct_PStruct(b *testing.B) {
for i := 0; i < b.N; i++ {
Struct(structPointer, &structPointerNil)
}
}
// struct -> *struct
func Benchmark_Reflect_PStruct_Struct(b *testing.B) {
for i := 0; i < b.N; i++ {
v1 := reflect.ValueOf(structPointer)
v2 := reflect.ValueOf(structObj)
//if v1.Kind() == reflect.Ptr {
// if elem := v1.Elem(); elem.Type() == v2.Type() {
// elem.Set(v2)
// }
//}
v1.Elem().Set(v2)
}
}
func Benchmark_Struct_PStruct_Struct(b *testing.B) {
for i := 0; i < b.N; i++ {
Struct(structObj, structPointer)
}
}
// []struct -> *[]struct
func Benchmark_Reflect_PStructs_Structs(b *testing.B) {
for i := 0; i < b.N; i++ {
v1 := reflect.ValueOf(&structSliceNil)
v2 := reflect.ValueOf(structSlice)
//if v1.Kind() == reflect.Ptr {
// if elem := v1.Elem(); elem.Type() == v2.Type() {
// elem.Set(v2)
// }
//}
v1.Elem().Set(v2)
}
}
func Benchmark_Structs_PStructs_Structs(b *testing.B) {
for i := 0; i < b.N; i++ {
Structs(structSlice, &structSliceNil)
}
}
// []*struct -> *[]*struct
func Benchmark_Reflect_PPStructs_PStructs(b *testing.B) {
for i := 0; i < b.N; i++ {
v1 := reflect.ValueOf(&structPointerSliceNil)
v2 := reflect.ValueOf(structPointerSlice)
//if v1.Kind() == reflect.Ptr {
// if elem := v1.Elem(); elem.Type() == v2.Type() {
// elem.Set(v2)
// }
//}
v1.Elem().Set(v2)
}
}
func Benchmark_Structs_PPStructs_PStructs(b *testing.B) {
for i := 0; i < b.N; i++ {
Structs(structPointerSlice, &structPointerSliceNil)
}
}

View File

@ -143,3 +143,34 @@ func Test_Struct_SliceWithTag(t *testing.T) {
t.Assert(users[1].NickName, "name2")
})
}
func Test_Structs_DirectReflectSet(t *testing.T) {
type A struct {
Id int
Name string
}
gtest.C(t, func(t *gtest.T) {
var (
a = []*A{
{Id: 1, Name: "john"},
{Id: 2, Name: "smith"},
}
b []*A
)
err := gconv.Structs(a, &b)
t.Assert(err, nil)
t.AssertEQ(a, b)
})
gtest.C(t, func(t *gtest.T) {
var (
a = []A{
{Id: 1, Name: "john"},
{Id: 2, Name: "smith"},
}
b []A
)
err := gconv.Structs(a, &b)
t.Assert(err, nil)
t.AssertEQ(a, b)
})
}

View File

@ -1003,3 +1003,35 @@ func Test_Struct_AttrStructHasTheSameTag(t *testing.T) {
t.Assert(order.Product.UpdatedAtFormat, "")
})
}
func Test_Struct_DirectReflectSet(t *testing.T) {
type A struct {
Id int
Name string
}
gtest.C(t, func(t *gtest.T) {
var (
a = &A{
Id: 1,
Name: "john",
}
b *A
)
err := gconv.Struct(a, &b)
t.Assert(err, nil)
t.AssertEQ(a, b)
})
gtest.C(t, func(t *gtest.T) {
var (
a = A{
Id: 1,
Name: "john",
}
b A
)
err := gconv.Struct(a, &b)
t.Assert(err, nil)
t.AssertEQ(a, b)
})
}