提高 golang 的反射效能

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。golang 的反射很慢。這個和它的 api 設計有關。在 java 裡面,我們一般使用反射都是這樣來弄的。
Field field = clazz.getField("hello");field.get(obj1);field.get(obj2);

這個取得的反射物件類型是 java.lang.reflect.Field。它是可以複用的。只要傳入不同的obj,就可以取得這個obj上對應的 field。但是 golang 的反射不是這樣設計的

type_ := reflect.TypeOf(obj)field, _ := type_.FieldByName("hello")

這裡取出來的 field 對象是 reflect.StructField 類型,但是它沒有辦法用來取得對應對象上的值。如果要取值,得用另外一套對object,而不是type的反射

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

這裡取出來的 fieldValue 類型是 reflect.Value,它是一個具體的值,而不是一個可複用的反射對象了。

這就很蛋疼了!每次反射都需要malloc這個reflect.Value結構體。golang的反射效能怎麼可能快?

Jsoniter 是 golang 實現的,基於反射的 JSON 解析器。其實現原理是用 reflect.Type 得出來的資訊來直接做反射,而不依賴於 reflect.ValueOf。具體是怎麼實現的呢?

結構體

先解決一個小問題。怎麼利用 reflect.StructField 取得對象上的值?

對應的代碼在: go/feature_reflect_object.go at master · json-iterator/go · GitHub

fieldPtr := uintptr(structPtr) + field.Offset

在 reflect.StructField 上有一個 Offset 的屬性。利用這個可以計算出欄位的指標值。我們可以寫一個小測試來驗證,這個是對的。

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

列印出來的訊息是 &{hello}

擷取 interface{} 的指標

如果對應的結構體是以 interface{} 傳進來的。還需要從 interface{} 上取得結構體的指標

type TestObj struct {field1 string}struct_ := &TestObj{}structInter := (interface{})(struct_)// emptyInterface is the header for an interface{} value.type emptyInterface struct {typ  *struct{}word unsafe.Pointer}structPtr := (*emptyInterface)(unsafe.Pointer(&structInter)).wordfield, _ := reflect.TypeOf(structInter).Elem().FieldByName("field1")field1Ptr := uintptr(structPtr) + field.Offset*((*string)(unsafe.Pointer(field1Ptr))) = "hello"fmt.Println(struct_)

Slice

搞定了結構體,接下來就是處理slice類型了。

對應的代碼在:go/feature_reflect_array.go at master · json-iterator/go · GitHub

type sliceHeader struct {Data unsafe.PointerLen  intCap  int}

slice 的秘訣在於取出指向數組頭部的指標,然後具體的元素,通過位移量來計算。

slice := []string{"hello", "world"}type sliceHeader struct {Data unsafe.PointerLen  intCap  int}header := (*sliceHeader)(unsafe.Pointer(&slice))fmt.Println(header.Len)elementType := reflect.TypeOf(slice).Elem()secondElementPtr := uintptr(header.Data) + elementType.Size()*((*string)(unsafe.Pointer(secondElementPtr))) = "!!!"fmt.Println(slice)

列印出來的內容

2[hello !!!]

Map

對於 Map 類型來說,沒有 reflect.ValueOf 之外的擷取其內容的方式。所以還是只能老老實實地用golang內建的值反射api。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.