golang的反射很慢,这个和它的api设计有关。

  1. type_ := reflect.ValueOf(obj)
  2. fieldValue := type_.FieldByName("hello")

这里取出来的fielValue类型是reflect.Value,它是一个具体的值,而不是一个可复用的反射对象。这样每次反射都需要malloc这个reflect.Value结构体。
Jsoniter是golang是实现的,基于反射的JSON解析器。其实原理是用reflect.Type得出来的信息来直接做反射。而不依赖于reflect.ValueOf。

具体做法

结构体
利用refect.StructField取得对象上的值,对应的代码在: go/feature_reflect_object.go at master · json-iterator/go · GitHub:

  1. fieldPtr := uintptr(structPtr) + field.Offset

在reflect.StructField上有一个Offset的属性。利用这个可以计算出字段的指针值。测试示例:

  1. type TestObj struct {
  2. field1 string
  3. }
  4. struct_ := &TestObj{}
  5. field, _ := reflect.TypeOf(struct_).Elem().FieldByName("field1")
  6. field1Ptr := uintptr(unsafe.Pointer(struct_)) + field.Offset
  7. *((*string)(unsafe.Pointer(field1Ptr))) = "hello"
  8. fmt.Println(struct_)

打印出来的消息是&{hello}

获取interface{}的指针
如果对应的结构体是以interface{}传进来的。还需要从interface{}上取得结构体的指针

  1. type TestObj struct {
  2. field1 string
  3. }
  4. struct_ := &TestObj{}
  5. structInter := (interface{})(struct_)
  6. // emptyInterface is the header for an interface{} value.
  7. type emptyInterface struct {
  8. typ *struct{}
  9. word unsafe.Pointer
  10. }
  11. structPtr := (*emptyInterface)(unsafe.Pointer(&structInter)).word
  12. field, _ := reflect.TypeOf(structInter).Elem().FieldByName("field1")
  13. field1Ptr := uintptr(structPtr) + field.Offset
  14. *((*string)(unsafe.Pointer(field1Ptr))) = "hello"
  15. fmt.Println(struct_)

slice类型

对应的代码在:go/feature_reflect_array.go at master · json-iterator/go · GitHub

  1. type sliceHeader struct {
  2. Data unsafe.Pointer
  3. Len int
  4. Cap int
  5. }

slice的秘密在于取出指向数组头部的指针,然后具体的元素,通过偏移量来计算。

  1. slice := []string{"hello", "world"}
  2. type sliceHeader struct {
  3. Data unsafe.Pointer
  4. Len int
  5. Cap int
  6. }
  7. header := (*sliceHeader)(unsafe.Pointer(&slice))
  8. fmt.Println(header.Len)
  9. elementType := reflect.TypeOf(slice).Elem()
  10. secondElementPtr := uintptr(header.Data) + elementType.Size()
  11. *((*string)(unsafe.Pointer(secondElementPtr))) = "!!!"
  12. fmt.Println(slice)

打印出来的内容:

  1. 2
  2. [hello !!!]
  3. Map

对于Map类型来说,没有reflect.ValueOf之外的获取其内容的方式。所以只能老老实实地用golang自带的值反射api。


golang 提高反射性能 - 图1