1 回答

TA貢獻1856條經驗 獲得超17個贊
// 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 和測試結果:
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 個月,所以如果我在上面的代碼中犯了任何 Golang 叛國罪,請寬容我。:)
- 1 回答
- 0 關注
- 93 瀏覽
添加回答
舉報