有大量反序列化时,目标 Struct 的成员用指针
JSON and Go 中介绍反序列化时说,介绍了目标 struct 中有 Reference Type 类型成员时的处理:
type FamilyMember struct {
Name string
Age int
Parents []string
}
var m FamilyMember
err := json.Unmarshal(b, &m)
FamilyMember 的 Parents 是一个 Slice,在反序列化时候,会为它分配空间。
重点是,如果目标字符串中没有 Parents,那么 Parents 是 nil。据此判断,反序列化的目标 struct 中应当尽可能的使用 Reference Type,在有缺失字段时,这种方式更高效且节省内存。
两种定义方式
Obj 定义了长度为 100 的数组,ObjP 使用的是 Slice:
package unmarshal
import "encoding/json"
type Obj struct {
Value int
Array [100]int
}
type ObjP struct {
Value int
Array []int
}
func UnmarshalObj(str []byte, v interface{}) {
json.Unmarshal(str, v)
}
func UnmarshalObjP(str []byte, v interface{}) {
json.Unmarshal(str, v)
}
基准测试结果
基准测试代码如下,模拟实际场景,每次反序列化都用一个新的变量,设计了 Array 有和无两种场景:
package unmarshal
import (
"testing"
)
var objstr_noarray = []byte(`{"Value":10}`)
var objstr_array = []byte(`{"Value":10, "Array":[1,2,3,4,5,7,8]}`)
func BenchmarkUnmarshalObjNoArray(b *testing.B) {
for i := 0; i < b.N; i++ {
obj := &Obj{}
UnmarshalObj(objstr_noarray, obj)
}
}
func BenchmarkUnmarshalObjPNoArray(b *testing.B) {
for i := 0; i < b.N; i++ {
obj := &ObjP{}
UnmarshalObjP(objstr_noarray, obj)
}
}
func BenchmarkUnmarshalObjArray(b *testing.B) {
for i := 0; i < b.N; i++ {
obj := &Obj{}
UnmarshalObj(objstr_array, obj)
}
}
func BenchmarkUnmarshalObjPArray(b *testing.B) {
for i := 0; i < b.N; i++ {
obj := &ObjP{}
UnmarshalObjP(objstr_array, obj)
}
}
基准测试结果显示,执行效率的差距非常明显:
goos: darwin
goarch: amd64
pkg: go-code-example/unmarshal
BenchmarkUnmarshalObjNoArray-4 1385480 861 ns/op
BenchmarkUnmarshalObjPNoArray-4 1712936 693 ns/op
BenchmarkUnmarshalObjArray-4 229220 4899 ns/op
BenchmarkUnmarshalObjPArray-4 367184 2883 ns/op
PASS