Golang 反射性能優化

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Golang的反射最爲人詬病的就是它極差的性能,接下來我們嘗試優化它的性能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們使用正常的流程來創建一個對象,將會是如下的代碼片段:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type People struct {\n Age int\n Name string\n}\n\nfunc New() *People {\n return &People{\n Age: 18,\n Name: \"shiina\",\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上的代碼非常好讀,但是如果我們要開發一款框架,接收的類型非常有可能是動態的、不確定的,那麼就會使用到"},{"type":"codeinline","content":[{"type":"text","text":"反射(Reflect)"}]},{"type":"text","text":"功能,使用反射來創建一個如上的"},{"type":"codeinline","content":[{"type":"text","text":"Person"}]},{"type":"text","text":"對象大概是如下的代碼片段:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func NewUseReflect() interface{} {\n var p People\n t := reflect.TypeOf(p)\n v := reflect.New(t)\n v.Elem().Field(0).Set(reflect.ValueOf(18))\n v.Elem().Field(1).Set(reflect.ValueOf(\"shiina\"))\n return v.Interface()\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上是一段普通的反射代碼,既然大家都說"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"的反射性能極差,那麼我們就來自己看一下它的性能和上一個我們正常創建"},{"type":"codeinline","content":[{"type":"text","text":"Person"}]},{"type":"text","text":"對象比性能差了多少。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"簡單的性能測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們先用"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"自帶的"},{"type":"codeinline","content":[{"type":"text","text":"go bench"}]},{"type":"text","text":"來分析一下它的性能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func BenchmarkNew(b *testing.B) {\n b.ReportAllocs()\n b.ResetTimer()\n for i := 0; i < b.N; i++ {\n New()\n }\n}\n\nfunc BenchmarkNewUseReflect(b *testing.B) {\n b.ReportAllocs()\n b.ResetTimer()\n for i := 0; i < b.N; i++ {\n NewUseReflect()\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們得到的測試結果如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"BenchmarkNew\nBenchmarkNew-16 1000000000 1.55 ns/op 0 B/op 0 allocs/op\nBenchmarkNewUseReflect\nBenchmarkNewUseReflect-16 4787185 248 ns/op 64 B/op 2 allocs/op"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們能夠發現使用反射的耗時是不使用的"},{"type":"text","marks":[{"type":"strong"}],"text":"160倍"},{"type":"text","text":"左右"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"性能損耗的猜測"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼反射創建對象,主要的性能損耗在哪裏呢?我們先進行一個實驗:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"並且當我們增加更多的結構體成員變量,比如增加兩個"},{"type":"codeinline","content":[{"type":"text","text":"string"}]},{"type":"text","text":"類型的成員變量,進行一次性能測試,然後再去掉所有的成員變量,進行一次性能測試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"四個成員變量:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type People struct {\n Age int\n Name string\n Test1 string\n Test2 string\n}\n\nfunc New() interface{} {\n return &People{\n Age: 18,\n Name: \"shiina\",\n Test1: \"test1\",\n Test2: \"test2\",\n }\n}\n\nfunc NewUseReflect() interface{} {\n var p People\n t := reflect.TypeOf(p)\n v := reflect.New(t)\n v.Elem().Field(0).Set(reflect.ValueOf(18))\n v.Elem().Field(1).Set(reflect.ValueOf(\"shiina\"))\n v.Elem().Field(2).Set(reflect.ValueOf(\"test1\"))\n v.Elem().Field(3).Set(reflect.ValueOf(\"test2\"))\n return v.Interface()\n}\n\n——————————————————————————————————————————\nBenchmarkNew\nBenchmarkNew-16 1000000000 1.12 ns/op 0 B/op 0 allocs/op\nBenchmarkNewUseReflect\nBenchmarkNewUseReflect-16 3334735 366 ns/op 128 B/op 2 allocs/op\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無成員變量:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type People struct{}\n\nfunc New() interface{} {\n return &People{}\n}\n\nfunc NewUseReflect() interface{} {\n var p People\n t := reflect.TypeOf(p)\n v := reflect.New(t)\n return v.Interface()\n}\n\n——————————————————————————————————————————\nBenchmarkNew\nBenchmarkNew-16 1000000000 1.32 ns/op 0 B/op 0 allocs/op\nBenchmarkNewUseReflect\nBenchmarkNewUseReflect-16 17362648 62.3 ns/op 0 B/op 0 allocs/op\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們猜測,反射性能的損耗具體分爲兩個部分,一個部分是"},{"type":"codeinline","content":[{"type":"text","text":"reflect.New()"}]},{"type":"text","text":",另一個部分是"},{"type":"codeinline","content":[{"type":"text","text":"value.Field().Set()"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這時候我們可以使用"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"原生自帶的性能分析工具"},{"type":"codeinline","content":[{"type":"text","text":"pprof"}]},{"type":"text","text":"來分析一下它們的主要耗時,來驗證我們的猜測。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們對四個成員變量測試用例使用"},{"type":"codeinline","content":[{"type":"text","text":"pprof"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"# 生成測試數據\nkieranhu@KIERANHU-MC0 ~/Downloads> go test -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out\n# 分析測試數據\nkieranhu@KIERANHU-MC0 ~/Downloads> go tool pprof ./profile.out\nType: cpu\nTime: Apr 24, 2020 at 7:38pm (CST)\nDuration: 2.02s, Total samples = 1.92s (94.91%)\nEntering interactive mode (type \"help\" for commands, \"o\" for options)\n(pprof) list NewUseReflect"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們使用pprof得到了該函數的主要耗時,可以發現與我們的猜測無誤,耗時主要分爲三個部分:"},{"type":"codeinline","content":[{"type":"text","text":"reflect.TypeOf()"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"reflect.New()"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"value.Field().Set()"}]},{"type":"text","text":",其中我們可以把"},{"type":"codeinline","content":[{"type":"text","text":"reflect.TypeOf()"}]},{"type":"text","text":"放到函數外,在初始化的時候生成,接下來我們主要關注"},{"type":"codeinline","content":[{"type":"text","text":"value.Fidle().Set()"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"ROUTINE ======================== begonia.NewUseReflect in /Users/kieranhu/go/src/begonia/reflect_test.go\n 60ms 2.17s (flat, cum) 64.97% of Total\n . . 29:\n 10ms 10ms 30:func NewUseReflect() interface{} {\n . . 31: var p People\n 10ms 580ms 32: t := reflect.TypeOf(p)\n . 440ms 33: v := reflect.New(t)\n 10ms 220ms 34: v.Elem().Field(0).Set(reflect.ValueOf(18))\n 10ms 250ms 35: v.Elem().Field(1).Set(reflect.ValueOf(\"shiina\"))\n . 280ms 36: v.Elem().Field(2).Set(reflect.ValueOf(\"test1\"))\n 10ms 220ms 37: v.Elem().Field(3).Set(reflect.ValueOf(\"test2\"))\n 10ms 170ms 38: return v.Interface()\n . . 39:}\n . . 40:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"幹掉 value.Field().Set()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先從怎麼不用xxx=xxx進行賦值說起。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"unsafe"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"中有一個包叫"},{"type":"codeinline","content":[{"type":"text","text":"unsafe"}]},{"type":"text","text":",顧名思義,它不安全,因爲它可以直接操作內存。我們可以使用"},{"type":"codeinline","content":[{"type":"text","text":"unsafe"}]},{"type":"text","text":",來對一個字符串進行賦值,具體的步驟大概如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"獲得該字符串的地址"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對該地址賦值"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們通過四行就可以完成上面的操作:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":" str := \"\"\n // 獲得該字符串的地址\n p := uintptr(unsafe.Pointer(&str))\n // 在該地址上賦值\n *(*string)(unsafe.Pointer(p))=\"test\"\n fmt.Println(str)\n-----------------\ntest\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們能夠使用"},{"type":"codeinline","content":[{"type":"text","text":"unsafe"}]},{"type":"text","text":"來操作內存時,就可以進一步嘗試操作結構體了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"操作結構體"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們通過上述代碼,得到一個結論:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"只要我們知道內存地址,就可以操作任意變量。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們可以嘗試去操作結構體了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"的結構體有以下的兩個特點:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結構體的成員變量是順序存儲的"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結構體第一個成員變量的地址就是該結構體的地址。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據以上兩點,以及剛剛我們得到的結論,我們可能夠得到以下的方法,來幹掉"},{"type":"codeinline","content":[{"type":"text","text":"value.Field().Set()"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"獲得結構體地址"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"獲得結構體內成員變量的偏移量"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"得到結構體成員變量地址"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改變量值"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們逐個來獲得獲得。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"中"},{"type":"codeinline","content":[{"type":"text","text":"interface"}]},{"type":"text","text":"類型是以這樣的形式保存的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// emptyInterface is the header for an interface{} value.\ntype emptyInterface struct {\n typ *rtype\n word unsafe.Pointer\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個結構體的定義可以在"},{"type":"codeinline","content":[{"type":"text","text":"reflect/Value.go"}]},{"type":"text","text":"找到。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個結構體中"},{"type":"codeinline","content":[{"type":"text","text":"typ"}]},{"type":"text","text":"是該"},{"type":"codeinline","content":[{"type":"text","text":"interface"}]},{"type":"text","text":"的具體類型,"},{"type":"codeinline","content":[{"type":"text","text":"word"}]},{"type":"text","text":"指針保存了指向結構體的地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在我們瞭解了"},{"type":"codeinline","content":[{"type":"text","text":"interface"}]},{"type":"text","text":"的存儲類型後,我們只需要將一個"},{"type":"codeinline","content":[{"type":"text","text":"空接口interface{}"}]},{"type":"text","text":"轉換爲"},{"type":"codeinline","content":[{"type":"text","text":"emptyInterface"}]},{"type":"text","text":"類型,然後得到其中的"},{"type":"codeinline","content":[{"type":"text","text":"word"}]},{"type":"text","text":",就可以拿到結構體的地址了,即解決了第一步。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"結構體類型強轉"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先用下面這段代碼示例,來解決一下不同結構體之間的轉換:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type Test1 struct {\n Test1 string\n}\n\ntype Test2 struct {\n test2 string\n}\n\nfunc TestStruct(t *testing.T) {\n t1 := Test1{\n Test1: \"hello\",\n }\n\n t2 := *(*Test2)(unsafe.Pointer(&t1))\n fmt.Println(t2)\n}\n----------------\n{hello}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們更換兩個結構體中的成員變量類型,再嘗試一下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type Test1 struct {\n a int32\n b []byte\n}\n\ntype Test2 struct {\n b int16\n a string\n}\n\nfunc TestStruct(t *testing.T) {\n t1 := Test1{\n a:1,\n b:[]byte(\"asdasd\"),\n }\n\n t2 := *(*Test2)(unsafe.Pointer(&t1))\n fmt.Println(t2)\n}\n----------------\n{1 asdasd}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以發現,後面這次嘗試兩個結構體的類型完全不同,但是其中int32和int16的存儲方式相同,[]byte和string的存儲方式相同,我們可以得出一個簡單的結論:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"不論類型簽名是否相同,只要底層存儲方式相同,我們就可以強制轉換,並且可以突破私有成員變量限制。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過上面我們得到的結論,可以將"},{"type":"codeinline","content":[{"type":"text","text":"reflect/value.go"}]},{"type":"text","text":"裏面的"},{"type":"codeinline","content":[{"type":"text","text":"emptyInterface"}]},{"type":"text","text":"類型複製出來。然後我們對"},{"type":"codeinline","content":[{"type":"text","text":"interface"}]},{"type":"text","text":"強轉並取到"},{"type":"codeinline","content":[{"type":"text","text":"word"}]},{"type":"text","text":",就可以拿到結構體的地址了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type emptyInterface struct {\n typ *struct{}\n word unsafe.Pointer\n}\n\nfunc TestStruct(t *testing.T) {\n var in interface{}\n in = People{\n Age: 18,\n Name: \"shiina\",\n Test1: \"test1\",\n Test2: \"test2\",\n }\n\n t2 := uintptr(((*emptyInterface)(unsafe.Pointer(&in))).word)\n *(*int)(unsafe.Pointer(t2))=111\n fmt.Println(in)\n}\n---------------\n{111 shiina test1 test2}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們獲取了結構體地址後,根據結構體地址,修改了結構體內第一個成員變量的值,接下來我們開始進行第二步:得到結構體成員變量的偏移量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"我們可以通過反射,來輕鬆的獲得每一個成員變量的偏移量,進而根據結構體的地址,獲得每一個成員變量的地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們獲得了每一個成員變量的地址後,就可以很輕易的修改它了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"var in interface{}\n in = People{\n Age: 18,\n Name: \"shiina\",\n Test1: \"test1\",\n Test2: \"test2\",\n }\n\n typeP := reflect.TypeOf(in)\n offset1 := typeP.Field(1).Offset\n offset2 := typeP.Field(2).Offset\n offset3 := typeP.Field(3).Offset\n\n t2 := uintptr(((*emptyInterface)(unsafe.Pointer(&in))).word)\n\n *(*int)(unsafe.Pointer(t2)) = 111\n *(*string)(unsafe.Pointer(t2 + offset1)) = \"hello\"\n *(*string)(unsafe.Pointer(t2 + offset2)) = \"hello1\"\n *(*string)(unsafe.Pointer(t2 + offset3)) = \"hello2\"\n fmt.Println(in)\n---------------------\n{111 hello hello1 hello2}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們剛剛成功的利用地址修改了結構體的成員變量,沒有使用到"},{"type":"codeinline","content":[{"type":"text","text":"value.Field().Set()"}]},{"type":"text","text":"。接下來我們利用剛剛的技巧,修改反射函數,並再次進行性能測試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們保留以前的反射函數做對比,新建一個"},{"type":"codeinline","content":[{"type":"text","text":"NewQuickReflect()"}]},{"type":"text","text":"來使用這種技巧創建對象:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"var (\n offset1 uintptr\n offset2 uintptr\n offset3 uintptr\n p People\n t = reflect.TypeOf(p)\n)\n\nfunc init() {\n offset1 = t.Field(1).Offset\n offset2 = t.Field(2).Offset\n offset3 = t.Field(3).Offset\n}\n\ntype People struct {\n Age int\n Name string\n Test1 string\n Test2 string\n}\n\ntype emptyInterface struct {\n typ *struct{}\n word unsafe.Pointer\n}\n\nfunc New() *People {\n return &People{\n Age: 18,\n Name: \"shiina\",\n Test1: \"test1\",\n Test2: \"test2\",\n }\n}\n\nfunc NewUseReflect() interface{} {\n v := reflect.New(t)\n\n v.Elem().Field(0).Set(reflect.ValueOf(18))\n v.Elem().Field(1).Set(reflect.ValueOf(\"shiina\"))\n v.Elem().Field(2).Set(reflect.ValueOf(\"test1\"))\n v.Elem().Field(3).Set(reflect.ValueOf(\"test2\"))\n return v.Interface()\n}\n\nfunc NewQuickReflect() interface{} {\n v := reflect.New(t)\n\n p := v.Interface()\n ptr0 := uintptr((*emptyInterface)(unsafe.Pointer(&p)).word)\n ptr1 := ptr0 + offset1\n ptr2 := ptr0 + offset2\n ptr3 := ptr0 + offset3\n *((*int)(unsafe.Pointer(ptr0))) = 18\n *((*string)(unsafe.Pointer(ptr1))) = \"shiina\"\n *((*string)(unsafe.Pointer(ptr2))) = \"test1\"\n *((*string)(unsafe.Pointer(ptr3))) = \"test2\"\n return p\n}\n\nfunc BenchmarkNew(b *testing.B) {\n b.ReportAllocs()\n b.ResetTimer()\n for i := 0; i < b.N; i++ {\n New()\n }\n}\n\nfunc BenchmarkNewUseReflect(b *testing.B) {\n b.ReportAllocs()\n b.ResetTimer()\n for i := 0; i < b.N; i++ {\n NewUseReflect()\n }\n}\n\nfunc BenchmarkNewQuickReflect(b *testing.B) {\n b.ReportAllocs()\n b.ResetTimer()\n for i := 0; i < b.N; i++ {\n NewQuickReflect()\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行後我們的測試結果:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"BenchmarkNew\nBenchmarkNew-16 1000000000 1.34 ns/op 0 B/op 0 allocs/op\nBenchmarkNewUseReflect\nBenchmarkNewUseReflect-16 3715539 276 ns/op 64 B/op 1 allocs/op\nBenchmarkNewQuickReflect\nBenchmarkNewQuickReflect-16 12772573 94.7 ns/op 64 B/op 1 allocs/op"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看出我們的性能從"},{"type":"text","marks":[{"type":"strong"}],"text":"原生205倍"},{"type":"text","text":"提升到了"},{"type":"text","marks":[{"type":"strong"}],"text":"70倍"},{"type":"text","text":",並且這個優化的程度將會隨着結構體成員變量越多而越明顯。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們對新寫的"},{"type":"codeinline","content":[{"type":"text","text":"NewQuickReflect"}]},{"type":"text","text":"函數使用"},{"type":"codeinline","content":[{"type":"text","text":"pprof"}]},{"type":"text","text":"分析一下,繼續觀察有沒有可以優化的點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"ROUTINE ======================== begonia.NewQuickReflect in /Users/kieranhu/go/src/begonia/reflect_test.go\n 120ms 1.07s (flat, cum) 28.53% of Total\n . . 57:\n . . 58:func NewQuickReflect() interface{} {\n 40ms 800ms 59: v := reflect.New(t)\n . . 60:\n . 180ms 61: p := v.Interface()\n . . 62: ptr0 := uintptr((*emptyInterface)(unsafe.Pointer(&p)).word)\n 40ms 40ms 63: ptr1 := ptr0 + offset1\n 10ms 10ms 64: ptr2 := ptr0 + offset2\n . . 65: ptr3 := ptr0 + offset3\n 10ms 10ms 66: *((*int)(unsafe.Pointer(ptr0))) = 18\n . 10ms 67: *((*string)(unsafe.Pointer(ptr1))) = \"shiina\"\n . . 68: *((*string)(unsafe.Pointer(ptr2))) = \"test1\"\n . . 69: *((*string)(unsafe.Pointer(ptr3))) = \"test2\"\n 20ms 20ms 70: return p\n . . 71:}\n . . 72:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們能夠發現最多的損耗花在了"},{"type":"codeinline","content":[{"type":"text","text":"reflect.New()"}]},{"type":"text","text":"上,我們着手嘗試對它進行優化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"幹掉 reflect.New()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"池化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於改善創建對象耗時來說,最簡單的優化方式便是"},{"type":"text","marks":[{"type":"strong"}],"text":"池化"},{"type":"text","text":",我們利用"},{"type":"codeinline","content":[{"type":"text","text":"sync.pool"}]},{"type":"text","text":"創建一個對象池,並且模擬對象池中資源充足的情況下的性能:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"var (\n /**\n ...........\n **/\n pool sync.Pool\n)\nfunc init() {\n /**\n ............\n **/\n pool.New = func() interface{} {\n return reflect.New(t)\n }\n for i := 0; i < 100; i++ {\n pool.Put(reflect.New(t).Elem())\n }\n}\n\n/**\n ............\n **/\n\nfunc NewQuickReflectWithPool() interface{} {\n p := pool.Get()\n\n ptr0 := uintptr((*emptyInterface)(unsafe.Pointer(&p)).word)\n ptr1 := ptr0 + offset1\n ptr2 := ptr0 + offset2\n ptr3 := ptr0 + offset3\n\n *((*int)(unsafe.Pointer(ptr0))) = 18\n *((*string)(unsafe.Pointer(ptr1))) = \"shiina\"\n *((*string)(unsafe.Pointer(ptr2))) = \"test1\"\n *((*string)(unsafe.Pointer(ptr3))) = \"test2\"\n return p\n}\n\nfunc BenchmarkQuickReflectWithPool(b *testing.B) {\n b.ReportAllocs()\n b.ResetTimer()\n for i := 0; i < b.N; i++ {\n obj := NewQuickReflectWithPool()\n pool.Put(obj)\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上述這個用例中,我們一拿到這個對象幾乎就立即放回了對象池,模擬的是對象池資源充足情況下的性能:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"BenchmarkNew\nBenchmarkNew-16 1000000000 1.26 ns/op 0 B/op 0 allocs/op\nBenchmarkNewUseReflect\nBenchmarkNewUseReflect-16 5515128 226 ns/op 64 B/op 1 allocs/op\nBenchmarkNewQuickReflect\nBenchmarkNewQuickReflect-16 21561645 91.4 ns/op 64 B/op 1 allocs/op\nBenchmarkQuickReflectWithPool\nBenchmarkQuickReflectWithPool-16 40770750 55.6 ns/op 0 B/op 0 allocs/op"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以發現在對象池對象充足的情況下,"},{"type":"text","marks":[{"type":"strong"}],"text":"沒有了malloc帶來的耗時"},{"type":"text","text":",我們的性能從"},{"type":"text","marks":[{"type":"strong"}],"text":"原生72倍"},{"type":"text","text":"提升到"},{"type":"text","marks":[{"type":"strong"}],"text":"原生的44倍"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是當對象池不充足情況下,就沒有這麼可喜的效率了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"另一個思路"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們能夠發現現在主要的耗時都在利用反射的創建對象上,這個時候我腦海裏有一個思路:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我們需要的是值類型(例如"},{"type":"codeinline","content":[{"type":"text","text":"Person{}"}]},{"type":"text","text":"),而不是指針的時候(例如"},{"type":"codeinline","content":[{"type":"text","text":"&Person"}]},{"type":"text","text":")時,我們是不是可以利用Go的這個特性:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"值類型傳遞值而不是指針的時候會進行拷貝"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來在使用反射的前提下,利用值傳遞特性獲得一個原生級別對象拷貝?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果不使用反射,已知類型的情況下會是如下的代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func TestStruct(t *testing.T) {\n p1 := People{}\n\n var p2 interface{}\n p2 = p1\n\n ptr0 := uintptr((*emptyInterface)(unsafe.Pointer(&p2)).word)\n ptr1 := ptr0 + offset1\n ptr2 := ptr0 + offset2\n ptr3 := ptr0 + offset3\n\n *((*int)(unsafe.Pointer(ptr0))) = 18\n *((*string)(unsafe.Pointer(ptr1))) = \"shiina\"\n *((*string)(unsafe.Pointer(ptr2))) = \"test1\"\n *((*string)(unsafe.Pointer(ptr3))) = \"test2\"\n\n fmt.Println(p1)\n fmt.Println(p2)\n}\n------------------------\n{0 }\n{18 shiina test1 test2}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以看到,我們使用這樣一個值傳遞的特性,得到了一份"},{"type":"codeinline","content":[{"type":"text","text":"p1"}]},{"type":"text","text":"的拷貝"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很可惜的是,當我們不能直接指定類型的時候,想象中這樣場景一直實現不了,會直接修改原變量的值,最終我找到了這樣的調用方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func TestNew(t *testing.T) {\n elemValue := reflect.New(reflect.TypeOf(People{})).Elem()\n p := elemValue.Interface()\n\n ptr0 := uintptr((*emptyInterface)(unsafe.Pointer(&p)).word)\n ptr1 := ptr0 + offset1\n ptr2 := ptr0 + offset2\n ptr3 := ptr0 + offset3\n\n *((*int)(unsafe.Pointer(ptr0))) = 18\n *((*string)(unsafe.Pointer(ptr1))) = \"shiina\"\n *((*string)(unsafe.Pointer(ptr2))) = \"test1\"\n *((*string)(unsafe.Pointer(ptr3))) = \"test2\"\n\n fmt.Println(p)\n fmt.Println(elemValue)\n}\n-------------------\n{18 shiina test1 test2}\n{0 }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每次"},{"type":"codeinline","content":[{"type":"text","text":"elemValue.Interface()"}]},{"type":"text","text":"時都會拷貝一個新的對象,這是我們期待的結果,接下來我們將它和之前的池化等一起進行性能測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"BenchmarkNew\nBenchmarkNew-16 1000000000 1.83 ns/op 0 B/op 0 allocs/op\nBenchmarkNewUseReflect\nBenchmarkNewUseReflect-16 2992928 372 ns/op 128 B/op 2 allocs/op\nBenchmarkNewQuickReflect\nBenchmarkNewQuickReflect-16 12648523 98.7 ns/op 64 B/op 1 allocs/op\nBenchmarkQuickReflectWithPool\nBenchmarkQuickReflectWithPool-16 40309711 58.2 ns/op 0 B/op 0 allocs/op\nBenchmarkNewWithElemReflect\nBenchmarkNewWithElemReflect-16 12700314 89.0 ns/op 64 B/op 1 allocs/op"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結果比較沮喪,我們僅提升了"},{"type":"text","marks":[{"type":"strong"}],"text":"不到10ns"},{"type":"text","text":",從"},{"type":"text","marks":[{"type":"strong"}],"text":"53倍"},{"type":"text","text":"提升到"},{"type":"text","marks":[{"type":"strong"}],"text":"48倍"},{"type":"text","text":","},{"type":"text","marks":[{"type":"strong"}],"text":"並且性能的提升也並不穩定"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲此我們閱讀"},{"type":"codeinline","content":[{"type":"text","text":"reflect.New()"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"elemValue.Interface()"}]},{"type":"text","text":"源碼,發現瞭如下的片段:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"reflect.New()"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func New(typ Type) Value {\n\tif typ == nil {\n\t\tpanic(\"reflect: New(nil)\")\n\t}\n\tt := typ.(*rtype)\n\tptr := unsafe_New(t)\n\tfl := flag(Ptr)\n\treturn Value{t.ptrTo(), ptr, fl}\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"elemValue.Interface()"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"if v.flag&flagAddr != 0 {\n // TODO: pass safe boolean from valueInterface so\n // we don't need to copy if safe==true?\n c := unsafe_New(t)\n typedmemmove(t, c, ptr)\n ptr = c\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"reflect.New()"}]},{"type":"text","text":"的主要耗時都在這個"},{"type":"codeinline","content":[{"type":"text","text":"unsafe_New()"}]},{"type":"text","text":"函數上,然而對於一個"},{"type":"codeinline","content":[{"type":"text","text":"elemValue"}]},{"type":"text","text":"取"},{"type":"codeinline","content":[{"type":"text","text":"Interface()"}]},{"type":"text","text":"時,反射還是會調用"},{"type":"codeinline","content":[{"type":"text","text":"unsafe_New()"}]},{"type":"text","text":"函數來創建一個新值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當多次實驗,性能測試之後,發現這種幹掉"},{"type":"codeinline","content":[{"type":"text","text":"reflect.New()"}]},{"type":"text","text":"的方式性能不夠穩定,基本沒有使用的必要。( T_T )"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"END"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上整個性能優化的從思路到實驗,再到實現大概總共花了一週的空閒時間。越寫越覺得我不像是在寫Go而是在寫c了。或許我應該讓Go寫的更像Go而不是想什麼黑魔法來讓Go更快(也更不安全)?很感謝需求不飽和讓我還有摸魚時間來研究這個(x"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章