最新消息:yaf表单扩展中新增加了浮点数、日期和集合的校验。php yaf框架扩展实践三——表单

Golang的简单反射性能测试

Golang 470浏览 0评论

测试用例

我们对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存在的情况下, 则不必太多担心.

转载请注明:快乐编程 » Golang的简单反射性能测试

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址