14

Consider working on an x64 bit Windows operation system with the following type alignments:

C++ build in types alignments on my pc

As far as I understand, it is very bad to do something like this:

struct X_chaotic
{
    bool flag1;
    double d1;
    bool flag2;
    double d2;
    bool flag3;
    double d3;
    //... and so on ...
};

According to C++ Alignment, Cache Line and Best Practice and Data structure alignment, it should be better/faster and much more compact to write this:

struct X_alignOrder
{
    double d1;
    double d2;
    double d3;
    //... all other doubles ...
    bool flag1;
    bool flag2;
    bool flag3;
    //... all other bools ...
};

The members are declared in the order of the alignment size, starting with the highest alignment.

Is it safe to say it is a good idea to order the declaration of the data members by alignment size? Would you say it is best practice? Or does it make no difference?

(I heard that the compiler can not rearrange the defined order, due to the C++ standard, and this even holds for all data members declared in access specifier blocks of a class)

Because I never read about this, neither in Scott Meyers' books nor in Bjarne Stroustrup's books, I wonder if I should start reordering the data declarations by alignment for my day-to-day work.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
user1911091
  • 1,219
  • 2
  • 14
  • 32

2 Answers2

9

This is more complicated than it may seem.

By ordering your members according to alignment needs you'll save some padding bytes and the total size will be smaller. This may be important to you if memory is tight or if this means the type can fit in a single cache line rather than two or three.

On the other hand; if you often access members that used to be close together so they would often be pulled into cache together by the CPUs prefetcher before, but now won't after reorganizing the class. Then you could be saving memory but sacrificing runtime performance.

Performance here may also vary greatly across different CPUs and different compilers/compiler options.

You'll need to run some benchmarks in your actual environment to see what performs the best for you.

Also keep in mind that reshuffling your member variables changes the order of initialization, which can be important if members depend on each other (foo initializes bar, so foo needs to be initialized first, etc).

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
  • 1
    *"This is more complicated than it may seem"*, In addition, that also changes order of initialization of members (with initializer list), and that might so change behaviour of the constructor if there are some dependencies between members. – Jarod42 Jul 28 '19 at 21:00
  • @Jarod42 Good point. Added a sentence to mention that. – Jesper Juhl Jul 28 '19 at 21:02
  • @Jarod42 So, the order of the items in the constructor initializer list doesn't matter? It's actually the order the members are declared in? – Jerry Jeremiah Jul 28 '19 at 22:49
  • 1
    @JerryJeremiah: Exactly, you might even have warning when both mismatch. – Jarod42 Jul 28 '19 at 22:51
4

Yes. In theory, the alignment of your data structures matters if you are concerned about the performance. It is good programming practice as well.

Most of the time, the alignment of your data structure is set based on the widest member of the 'struct'. Normally, your compiler takes care of it for you. The behaviour, however, can be different for C++ and C when in comes to inserting leading padding.

You can use the offsetof macro to evaluate the distance of a given struct member in size_t. This is ANSI C, though.

#include <stdio.h>
#include <stddef.h>

typedef struct Test_t {
    char *p;
    char c;
    int i;
    long l;
} Test;

int main(){
    printf("offsetof(Test,p) = %zu\n", offsetof(Test,p));
    printf("offsetof(Test,c) = %zu\n", offsetof(Test,c));
    printf("offsetof(Test,i) = %zu\n", offsetof(Test,i));
    printf("offsetof(Test,l) = %zu\n", offsetof(Test,l));
    return 0;
}

This will print

offsetof(Test,p) = 0
offsetof(Test,c) = 8
offsetof(Test,i) = 12
offsetof(Test,l) = 16
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
fnisi
  • 1,181
  • 1
  • 14
  • 24