如何优化Go反射函数?实测反射实现比手动实现慢2.5倍
Go语言反射实现部分更新的性能问题与优化建议
我正在开发一个项目,需要为所有支持的方法实现部分更新功能。每个部分更新对应不同结构体,字段数量与类型各异,且无法预知字段是否存在。我需要遍历结构体字段,将非空字段以[key,value]格式存入二维数组后返回。所有结构体字段均为指针类型,我编写了两种实现方式:
- 手动映射:为每个部分更新结构体编写单独的映射函数,逐个检查字段是否为nil,非空则加入结果数组;
- 泛型+反射:实现通用映射函数,完成相同逻辑。
相关代码
// main.go package main import ( "reflect" "strings" "time" ) type updateRequest struct { FieldOne *string `json:"field_one,omitempty"` FieldTwo *string `json:"field_two,omitempty"` FieldThree *string `json:"field_three,omitempty"` FieldFour *string `json:"field_four,omitempty"` FieldFive *string `json:"field_five,omitempty"` FieldSix *time.Time `json:"field_six,omitempty" time_format:"2006-01-02"` } // Mapping function that would need to be recreated for each partial update struct. func ManualUpdateRequestMapping(req *updateRequest) [][]string { vals := make([][]string, 0, 6) if req.FieldOne != nil { vals = append(vals, []string{"field_one", *req.FieldOne}) } if req.FieldTwo != nil && req.FieldThree != nil { vals = append(vals, []string{"field_two", *req.FieldTwo}, []string{"field_three", *req.FieldThree}) } if req.FieldFour != nil { vals = append(vals, []string{"field_four", *req.FieldFour}) } if req.FieldFive != nil { vals = append(vals, []string{"field_five", *req.FieldFive}) } if req.FieldSix != nil { vals = append(vals, []string{"field_six", req.FieldSix.Format(time.RFC3339)}) } return vals } // Generics and Reflection function func ReflectUpdateRequestMapping[T *updateRequest](str T) [][]string { valHolder := reflect.ValueOf(*str) if valHolder.Kind() != reflect.Struct { return nil } vals := make([][]string, 0, valHolder.NumField()) for i := 0; i < valHolder.NumField(); i++ { if valHolder.Field(i).IsNil() { continue } spl := strings.Split(valHolder.Type().Field(i).Tag.Get("json"), ",") if valHolder.Field(i).Elem().Type() != reflect.TypeOf(time.Time{}) { vals = append(vals, []string{spl[0], valHolder.Field(i).Elem().String()}) } else { vals = append(vals, []string{spl[0], valHolder.Field(i).Interface().(*time.Time).Format(time.RFC3339)}) } } return vals }
基准测试代码
// main_test.go package main import ( "testing" "time" ) func BenchmarkBoth(b *testing.B) { field1 := "testfield1" field2 := "testfield2" field3 := "testfield3" field4 := "testfield4" field5 := "testfield5" date1, _ := time.Parse(time.RFC3339, "2004-10-16T12:40:53.00Z") str := &updateRequest{ FieldOne: &field1, FieldTwo: &field2, FieldThree: &field3, FieldFour: &field4, FieldFive: &field5, FieldSix: &date1, } b.Run("ManualUpdateRequestMapping", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = ManualUpdateRequestMapping(str) } }) b.Run("ReflectUpdateRequestMapping", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = ReflectUpdateRequestMapping(str) } }) }
基准测试结果
cpu: 12th Gen Intel(R) Core(TM) i9-12900KF BenchmarkBoth/ManualUpdateRequestMapping-24 3560083 331.9 ns/op 368 B/op 8 allocs/op BenchmarkBoth/ReflectUpdateRequestMapping-24 1393377 866.7 ns/op 648 B/op 21 allocs/op PASS ok com.example.stack 3.745s
我原本预期反射函数会更慢,但没想到慢了约2.5倍,内存分配也多了约2.5倍。请问是我的代码存在问题,还是反射本身就有这样的性能损耗?同时希望得到优化该反射函数的建议。我接触Go语言约3个月,若代码存在不当之处请见谅。
内容的提问来源于stack exchange,提问作者JDev
相关产品推荐
相关产品推荐

