1. 程式人生 > >Golang的簡單反射效能測試

Golang的簡單反射效能測試

測試用例

我們對Golang的結構體變數賦值, 以及單引數函式呼叫進行反射和native操作的測試

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)})

}

}

效能測試資料

=== 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)

測試評測

  • 在結構體變數賦值測試用例中, 我們發現TestReflectFindFieldAndAssign賦值格外的耗時. 分析效能點在FieldByName這個函式上, 我們查了下底層如何實現的:

// 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來遍歷獲取資料, 但冷靜下來分析. 這樣做無可厚非.
試想如果reflect包在我們使用ValueOf時使用map緩衝好一個結構體所有欄位的訪問資料後, 肯定訪問指定欄位速度會很快
但是, 以空間換速度的需求其實最多滿足了1%的需求.
同樣的例子是圖形API裡訪問Shader變數的方法, 總是預設使用字串獲取, 速度很慢. 當你想快速訪問時, 請提前按需快取欄位
那麼, Golang使用的也是這樣的思路. 雖然暴力了一點, 但是能夠讓程式跑對, 效能優化的東西放在之後來做, 緩衝下就可以解決

  • 在呼叫測試用例中, 毫無懸念的, 呼叫速度很慢
    因此, 我們在平時使用反射時, 儘量偏向於反射變數緩衝存在下的變數賦值或者獲取
    而呼叫的需求儘量減少, 如果有goroutine存在的情況下, 則不必太多擔心.