bgarrood
bgarrood

Reputation: 497

Most efficient way to initialise variables in C

I am working on an embedded application in which RAM availability is very limited. In analysing the memory consumption I see that the bss segment is rather large and researching more it sounds like this can be due to lack of variable initialisation.

This is an example of the sort of thing I would normally do. Lets say I have this struct:

typedef struct
{
    float time;
    float value;
} pair_t;

I normally don't initialise variables on declaration, so instead do something like this:

pair_t test()
{
    pair_t ret;
    ret.time = 0;
    ret.value = 0;
    return ret;
}

Would I be better off doing it all in one, like this? Or does it make no difference?

pair_t test()
{
    pair_t ret = (pair_t)
    {
        .time = 0,
        .value = 0
    };
    return ret;
}

Upvotes: 3

Views: 220

Answers (2)

TL;DR

if you care only about zeroing the structure, the least to write is = {0} in initializing the structure, and most probably this will also result in the best code, except in cases where you do not want to initialize all members; to not initialize all members you must use the strategy 1, i.e. assignments to members.


A good implementation can notice that each variant has the same side effects and generate identical code for each option if optimizations are enabled. A bad implementation might not, just as it is very much easier for us humans to see that 0 is zero than it is to see that e^iπ + 1

is zero too.

Notice that

pair_t test()
{
    pair_t ret = (pair_t)
    {
        .time = 0,
        .value = 0
    };
    return ret;
}

additionally creates a compound literal of type pair_t which is unused. But a bad compiler might keep it around. The proper initialization code is

pair_t ret = {
    .time = 0,
    .value = 0
};

without that cast-looking thing.

And it might even be cheaper to use the simple zero initializer

pair_t ret = { 0 };

which should have the same effect but is even more explicitly zero.

At least MSVC seems to be tricked without optimizations enabled, c.f. this one.

But when optimizations enabled (-O3), GCC 9.1 x86-64 will generate

test:
        pxor    xmm0, xmm0
        ret

for all options, but again this is a quality-of-implementation issue, for a compiler of inferior quality (MSVC 19.14) might generate shortest code for only strictly default-initializer {0}:

ret$ = 8
test    PROC                                            ; COMDAT
        xor     eax, eax
        mov     QWORD PTR ret$[rsp], rax
        ret     0
test    ENDP

If you compare this with using the strictly correct {0.0f, 0.0f}:

test    PROC                                            ; COMDAT
        xorps   xmm1, xmm1
        xorps   xmm0, xmm0
        unpcklps xmm0, xmm1
        movq    rax, xmm0
        ret     0
test    ENDP

Upvotes: 1

user149341
user149341

Reputation:

In analysing the memory consumption I see that the bss segment is rather large and researching more it sounds like this can be due to lack of variable initialisation.

A large bss segment simply means that your application has a lot of global and/or static variables (or some very large ones). These variables are in the bss segment because they are initialized to zero, or are left uninitialized -- but initializing them to nonzero values will just make them move to the data segment, which also resides in memory. If the memory usage of these variables is an issue, you need to use less of them. Changing the way they're initialized won't help.

That all being said, the variable you're dealing with here isn't in the bss segment at all, because it's a local variable. And there is absolutely no difference between initializing the variable when it's declared and initializing it explicitly with assignment statements; any sensible compiler will generate the exact same code for both.

Upvotes: 3

Related Questions