我正在開發一個專案,我需要為將支持的所有方法構建部分更新。每個部分更新都需要不同的結構,具有不同的欄位和欄位數量,并且不知道哪些將存在或不存在。我決定檢查每個結構欄位,如果存在,則將其添加到陣列中以在最后回傳。我還花了一些時間對幾個似乎最現實的函式進行基準測驗,以解決這個問題,并幫助做出決定。
所有結構欄位都是指標。考慮到這一點,這些是我撰寫的函式。
注意:我不能為此創建一個游樂場示例,因為它不支持基準測驗。我將鏈接完整的課程,并將解釋放在它們之上。
- 為每個部分更新結構創建一個映射函式,我將在其中分別檢查每個欄位。如果該欄位不是
nil,我會將值放入以 [key,value] 格式存盤的二維陣列中。處理完結構后,回傳陣列。 - 創建一個使用泛型和反射的映射函式來執行與上述相同的操作。
// 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 叛國罪,請放輕松。:)
uj5u.com熱心網友回復:
反射比非反射代碼慢。這是一個改進。一些注意事項:
- 通過將欄位作為常規型別的值并從那里作業來減少反射呼叫。
- 不需要那些新奇的型別引數的東西。
- 說起 Golang 叛逆,語言的名字叫 Go。
順便說一句,這里是代碼:
func UpdateRequestMapping(p any) [][]string {
v := reflect.ValueOf(p).Elem()
if v.Kind() != reflect.Struct {
return nil
}
t := v.Type()
result := make([][]string, t.NumField())
for i := 0; i < t.NumField(); i {
var ok bool
var value string
switch f := v.Field(i).Interface().(type) {
case *string:
if f != nil {
ok = true
value = *f
}
case *time.Time:
if f != nil {
ok = true
value = (*f).Format(time.RFC3339)
}
}
if ok {
name, _, _ := strings.Cut(t.Field(i).Tag.Get("json"), ",")
result[i] = []string{name, value}
}
}
return result
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/519858.html
標籤:表现去反射去反思
下一篇:如果基礎集合是HashSet<T>而不是List<T>,C#集合操作(??Union、Intersect、Except、DIstinct)是否更快?
