[Golang言語コミュニティ] -golangの反射パフォーマンスを改善します



Improve Reflection Performance Golang



Golangの反射は非常に遅いです。これは、APIの設計に関連しています。 Javaでは、通常、リフレクションを使用してこれを行います。Field field = clazz.getField('hello') field.get(obj1) field.get(obj2)

取得されるリフレクションオブジェクトタイプはjava.lang.reflect.Fieldです。再利用できます。別のオブジェクトを渡す限り、このオブジェクトの対応するフィールドを取得できます。しかし、golangの反射はこのように設計されていません

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

ここで取得されるフィールドオブジェクトはreflect.StructFieldタイプですが、対応するオブジェクトの値を取得するために使用することはできません。値を取得する場合は、タイプの代わりにオブジェクトに別の反射のセットを使用する必要があります



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

ここで取り出されたfieldValueタイプはreflect.Valueです。これは特定の値であり、再利用可能なリフレクションオブジェクトではありません。

これはとても痛いです! すべてのリフレクションにはreflect.Value構造mallocが必要です。 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 {}を介して渡された場合。また、インターフェイスから構造体のポインタを取得する必要があります{}

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)).word field, _ := reflect.TypeOf(structInter).Elem().FieldByName('field1') field1Ptr := uintptr(structPtr) + field.Offset *((*string)(unsafe.Pointer(field1Ptr))) = 'hello' fmt.Println(struct_)

スライス

構造が完成したら、次のステップはスライスタイプを処理することです。

対応するコードは次のとおりです。 go / feature_reflect_array.go at master・json-iterator / go・GitHub

type sliceHeader struct { Data unsafe.Pointer Len int Cap int }

スライスの秘訣は、配列の先頭へのポインタを取り出し、オフセットによって特定の要素を計算することです。

slice := []string{'hello', 'world'} type sliceHeader struct { Data unsafe.Pointer Len int Cap 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 !!!]

地図

マップタイプの場合、reflect.ValueOf以外のコンテンツを取得する方法はありません。したがって、golangに付属している値リフレクションAPIのみを正直に使用できます。