How to implement the transformation of pointer of struct member pointer to struct body in Golang

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Original address: http://goworldgs.com/?p=37

In C, there is a classic macro definition that converts a pointer to a member of the struct struct itself to a pointer to the struct itself. Here is an example of a Field_offset macro that calculates the offset of a field within a structure, and the function Gett can obtain a corresponding t* object from a f* pointer.

struct F {    int c;    int d;} struct T{    int a;    int b;    struct F f;} #define FIELD_OFFSET(type, field) ((int)(unsigned char *)(((struct type *)0)->field)) struct T* getT(struct F* f) {    return (T*)((unsigned char *)f - FIELD_OFFSET(T, F))}


Can the same function be achieved in Golang? Try to write the following code:

type T struct {a intb intf F}type F struct {c intd int}func (m *F) T1() *T {var dummy *TfieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))}

Compile through, run! Panic:runtime Error:invalid memory address or nil pointer dereference. Here dummy *t is nil, although the code does not access the content that dummy points to, but Golang still does not allow this pointer to be used.

Since Golang does not allow the use of the nil pointer, we can bypass the problem by creating a useless T object, with the following code:

func (m *F) T2() *T {var dummy TfieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(&dummy))return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))}

The test proves that this code works, and we can use another function Tbad to compare the performance:

func (m *F) TBad() *T {return (*T)(unsafe.Pointer(uintptr(unsafe.Pointer(m)) - 16))} func BenchmarkGetPtrByMemberPtr_T2(b *testing.B) {var t Tfor i := 0; i < b.N; i++ {if &t != t.f.T2() {b.Fatal("wrong")}}} func BenchmarkGetPtrByMemberPtr_TBad(b *testing.B) {var t Tfor i := 0; i < b.N; i++ {if &t != t.f.TBad() {b.Fatal("wrong")}}}

Test results: The operating costs of T2 and Tbad are: 1.44 ns/op and 0.85 ns/op respectively.

Considering why T2 is more expensive than Tbad, we suspect that T2 will need to create a T object on the heap every time. If the size of the T object is large, the overhead of creating a T object increases, and we can verify by increasing the size of the struct T. We modify the definition of the T struct to:

type T struct {a intb intf Fe [1024]byte}

Running again discovers that the T2 overhead increases to 37.8 ns/op. So how can we eliminate the effect of the T struct size on this function? Golang does not allow us to use the nil pointer, is it true that we only need to forge a *t non-nil pointer? Try writing the following code and testing:

func (m *F) T3() *T {var x struct{}dummy := (*T)(unsafe.Pointer(&x))fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))}


T3 's overhead dropped to 1.14 ns/op, near the fastest Tbad 0.85 ns/op. Further, we can use the *f pointer directly as the dummy, the code is as follows:

func (m *F) T4() *T {dummy := (*T)(unsafe.Pointer(m))fieldOffset := uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))}


But the tests show that T4 and T3 have exactly the same overhead, 1.14 ns/op.

So far, T3 and T4 have achieved very good performance, just a little bit higher than the Tbad. The reason for this is that Tbad does not need to calculate the offset of the F Type field, in C the Field_offset macro is also evaluated at compile time, but in T3 and T4 it is necessary to calculate the offset of the F *f field once in the T structure. We can use a global variable to hold the offset of the field so that it does not need to be evaluated every time, the code is as follows:

var fieldOffset uintptr func init() {dummy := (*T)(unsafe.Pointer(&fieldOffset))fieldOffset = uintptr(unsafe.Pointer(&dummy.f)) - uintptr(unsafe.Pointer(dummy))}func (m *F) T5() *T {return (*T)(unsafe.Pointer((uintptr)(unsafe.Pointer(m)) - fieldOffset))}

The test shows that the cost of T5 is the same as Tbad, which is 0.85 ns/op, which should be the limit.

Because the go language does not provide a generic mechanism, each class that needs to use this functionality needs to define its own conversion function, rather than using a generic macro like C/T.

If you have a better plan, please leave a message to tell me!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.