C++Guns – RoboBlog

13.01.2019

C++ Guns: NOT another level of indirection

Filed under: Allgemein — Tags: — Thomas @ 11:01

The following code snips does NOT create a level of indirection. You can see it by introspect the assembler code.
So don't worry, start structure your data. See the next post for a practice example.

Example 1

Lets begin with a simple struct contains 3 members: double, float, int. Sum them up. And see what the compile generate

struct A {
    double d;
    float f;
    int i;
};

static_assert(sizeof(A) == 16);

auto func(const A& a){
    return a.d + a.f + a.i;
}

With the static assert sizeof you can be sure there is no padding in the data structure. The generate ASM code compiled with GCC 8.1 -O1 I comment it to make it easier for you.

func(A const&):
        pxor    %xmm0, %xmm0              # xmm0 = 0
        cvtss2sd        8(%rdi), %xmm0    # convert float to double and store it in xmm0
        addsd   (%rdi), %xmm0             # add double and the converted float to xmm0
        pxor    %xmm1, %xmm1              # xmm1 = 0
        cvtsi2sd        12(%rdi), %xmm1   # convert integer to double and store it in xmm1
        addsd   %xmm1, %xmm0              # add the already added float+double to the converted integer to xmm0
        ret                               # return xmm0 as result

You can see two conversions from int/float to double and two additions. You can also see the offsets. The double value has offset 0, the float value 8 bytes and the integer value (8+4)=12 bytes. This is quite straight. So add more code.

Example 2

This example create a nested struct Bdata inside struct B. Sum the values up again and see what the compiler generate.

struct B {
    struct Bdata {
        double d;
        float f;
        int i;
    };

    Bdata b;
};

static_assert(sizeof(B) == 16);

auto func2(const B& a){
    return a.b.d + a.b.f + a.b.i;
}

It is exactly, 100%, the same assembler code as example1 ! So i don't show it here again. The memory layout of struct B is the same as struct A, so the same ASM code is generated. No overhead by the extra variable b.

Example 3

In the next example a std::tuple is used to store the 3 values. The access is provided with the function std::get. It looks different. But wait...

struct C {
    std::tuple<double, float, int> c;
};

static_assert(sizeof(C) == 16);

auto func3(const C& a){
    return std::get<0>(a.c) + std::get<1>(a.c) + std::get<2>(a.c);
}

This time, the generated code is different

func3(C const&):
        pxor    %xmm0, %xmm0             # xmm0 = 0
        cvtss2sd        4(%rdi), %xmm0   # convert float to double and store it in xmm0
        addsd   8(%rdi), %xmm0           # add double and the converted float to xmm0
        pxor    %xmm1, %xmm1             # xmm1 = 0
        cvtsi2sd        (%rdi), %xmm1    # convert integer to double and store it in xmm1
        addsd   %xmm1, %xmm0             # add the already added float+double to the converted integer to xmm0
        ret                              # return xmm0 as result

But if you take a closer look, it is still the same! Only the memory layout has changed. First the integer, then the float, then the double. A std::stuple store it's values in reverse order! But the generated assembler code is 100% identical to the first example!

Example 4

In the last example I change the structure a little bit. But you can guess, the result is still the same.

struct D {
    double d;

    struct Ddata {    
        float f;
        int i;
    };

    Ddata x;
};

static_assert(sizeof(D) == 16);

auto func4(const D& a){
    return a.d + a.x.f + a.x.i;
}

See the next post for a practice example.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress