測(cè)試用例
我們對(duì)Golang的結(jié)構(gòu)體變量賦值, 以及單參數(shù)函數(shù)調(diào)用進(jìn)行反射和native操作的測(cè)試
package main
import (
"reflect"
"testing"
)
type data struct {
Hp int
}
const AssignTimes = 100000000
func TestNativeAssign(t *testing.T) {
v := data{Hp: 2}
for i := 0; i < AssignTimes; i++ {
v.Hp = 3
}
}
func TestReflectAssign(t *testing.T) {
v := data{Hp: 2}
vv := reflect.ValueOf(&v).Elem()
f := vv.FieldByName("Hp")
for i := 0; i < AssignTimes; i++ {
f.SetInt(3)
}
}
func TestReflectFindFieldAndAssign(t *testing.T) {
v := data{Hp: 2}
vv := reflect.ValueOf(&v).Elem()
for i := 0; i < AssignTimes; i++ {
vv.FieldByName("Hp").SetInt(3)
}
}
func foo(v int) {
}
const CallTimes = 100000000
func TestNativeCall(t *testing.T) {
for i := 0; i < CallTimes; i++ {
foo(i)
}
}
func TestReflectCall(t *testing.T) {
v := reflect.ValueOf(foo)
for i := 0; i < CallTimes; i++ {
v.Call([]reflect.Value{reflect.ValueOf(2)})
}
}
性能測(cè)試數(shù)據(jù)
=== RUN TestNativeAssign
— PASS: TestNativeAssign (0.03s)
=== RUN TestReflectAssign
— PASS: TestReflectAssign (0.41s)
=== RUN TestReflectFindFieldAndAssign
— PASS: TestReflectFindFieldAndAssign (9.86s)
=== RUN TestNativeCall
— PASS: TestNativeCall (0.03s)
=== RUN TestReflectCall
— PASS: TestReflectCall (21.46s)
測(cè)試評(píng)測(cè)
- 在結(jié)構(gòu)體變量賦值測(cè)試用例中, 我們發(fā)現(xiàn)TestReflectFindFieldAndAssign賦值格外的耗時(shí). 分析性能點(diǎn)在FieldByName這個(gè)函數(shù)上, 我們查了下底層如何實(shí)現(xiàn)的:
// FieldByName returns the struct field with the given name
// and a boolean to indicate if the field was found.
func (t *structType) FieldByName(name string) (f StructField, present bool) {
// Quick check for top-level name, or struct without anonymous fields.
hasAnon := false
if name != "" {
for i := range t.fields {
tf := &t.fields[i]
if tf.name == nil {
hasAnon = true
continue
}
if *tf.name == name {
return t.Field(i), true
}
}
}
if !hasAnon {
return
}
return t.FieldByNameFunc(func(s string) bool { return s == name })
}
各位看官必須吐槽用for來遍歷獲取數(shù)據(jù), 但冷靜下來分析. 這樣做無可厚非.
試想如果reflect包在我們使用ValueOf時(shí)使用map緩沖好一個(gè)結(jié)構(gòu)體所有字段的訪問數(shù)據(jù)后, 肯定訪問指定字段速度會(huì)很快
但是, 以空間換速度的需求其實(shí)最多滿足了1%的需求.
同樣的例子是圖形API里訪問Shader變量的方法, 總是默認(rèn)使用字符串獲取, 速度很慢. 當(dāng)你想快速訪問時(shí), 請(qǐng)?zhí)崆鞍葱杈彺孀侄?br>那么, Golang使用的也是這樣的思路. 雖然暴力了一點(diǎn), 但是能夠讓程序跑對(duì), 性能優(yōu)化的東西放在之后來做, 緩沖下就可以解決
- 在調(diào)用測(cè)試用例中, 毫無懸念的, 調(diào)用速度很慢
因此, 我們?cè)谄綍r(shí)使用反射時(shí), 盡量偏向于反射變量緩沖存在下的變量賦值或者獲取
而調(diào)用的需求盡量減少, 如果有g(shù)oroutine存在的情況下, 則不必太多擔(dān)心.