C++ Weekly - Ep 410 - What Are Padding and Alignment? (And Why You Might Care)

แชร์
ฝัง
  • เผยแพร่เมื่อ 28 ก.ย. 2024

ความคิดเห็น • 88

  • @kuhluhOG
    @kuhluhOG 8 หลายเดือนก่อน +49

    One tip when rearranging data members to avoid padding: Put your biggest members FIRST.

    • @Minty_Meeo
      @Minty_Meeo 8 หลายเดือนก่อน +19

      Dude Jason was driving me nuts in this episode by doing the complete opposite of this!

  • @stephenhowe4107
    @stephenhowe4107 8 หลายเดือนก่อน +29

    1. I generally arrange largest to smallest members in a struct. That means that 1-byte padding after the i would not occur.
    2. But there is other padding as well. There is often padding at the end of the struct. This is so, if you have an array of this struct, the next item will start on the proper boundary.

    • @stephenhowe4107
      @stephenhowe4107 8 หลายเดือนก่อน +1

      By largest to smallest members, I mean by type. So largest type first, smallest type last.

  • @victotronics
    @victotronics 8 หลายเดือนก่อน +93

    Next episode: what are cachelines and why do want to align for them?

    • @alexvergara2491
      @alexvergara2491 8 หลายเดือนก่อน +2

      BEST comment and idea

    • @dmitriidemenev5258
      @dmitriidemenev5258 8 หลายเดือนก่อน +10

      2nd next episode: what is endianness or byte order. Also, the monstrosity of the past - mixed-endianness.

  • @bernardmayeur3952
    @bernardmayeur3952 8 หลายเดือนก่อน +11

    @Jason, why not start with the biggest type first? By starting with the biggest you always have the most optimal struct size right? 8+4+4+1+1+1 (+1 for alignment here) = 20 or 24

    • @bernardmayeur3952
      @bernardmayeur3952 8 หลายเดือนก่อน

      Never mind. Yours will also work. I was thinking that packing biggest first was a best practice for cache. Seems both way works

    • @HinaraT
      @HinaraT 8 หลายเดือนก่อน +3

      Yes but your solution prevent the padded warning for clang on the fields and instead put it on the struct

  • @snbv5real
    @snbv5real 8 หลายเดือนก่อน +8

    I don't usually use packed structs... *if I'm in control of the struct definition*. I usually manually pack the struct and static_assert() the size is what I manually calculated, this comes up a *lot* in Vulkan/OpenGL/ anything that touches GLSL, where default alignment rules are not like C++, and default to *large* padding amounts. In the case of OpenGL/Vulkan code, I will often actually manually insert padding variables because those rules don't line up with C++. For a lot of other uses, in particular *data serialization*, I'm forced to pack structures, because the structures are *defined* to be packed. There's a data format I have to use a lot, that *must be packed* when over the wire or stored via io, otherwise none of the functions de-serializing it will work. It's sad, because the structure *could* have been re-arranged such that packing was unnecessary, though the order of elements may not have been as human friendly.

  • @danielkatzav9971
    @danielkatzav9971 8 หลายเดือนก่อน +1

    Hey Jason ! I was actually waiting for a good video from you about this matter
    I do use packed struct - but as part of multiple embedded environments.
    I would be happy share my thoughts and takes and learn from that experience.

  • @anon_y_mousse
    @anon_y_mousse 7 หลายเดือนก่อน

    Your manipulation of tabular data had me wondering if CE could do blockwise selections, since it has a limited subset of Vim keybindings, and apparently it can. So, just a tiny tip for you for next time you want to demonstrate editing a table of data, such as comments at the end of every line, you can just hit ctrl+v and select the entire block at once then x or d to delete it. You just need to enable Vim keybindings in the interface.

  • @nicholaskomsa1777
    @nicholaskomsa1777 4 หลายเดือนก่อน

    first of all, I'd more than likely be using items like std::uint16_t or std::uint32_t, and then std::size_t. Your code appears depreciated.

    • @cppweekly
      @cppweekly  4 หลายเดือนก่อน

      Not at all - size_t means "native machine size type" this is the correct thing to do for crossplatform code. int32, etc, should be used when I want to specify the exact type right now.

    • @nicholaskomsa1777
      @nicholaskomsa1777 4 หลายเดือนก่อน

      @@cppweekly Rather than using c-style data types that aren't quite so clear about their properties, which you describe in your video. Instead, if you want to clarify them - which is always - you use std data types and constructs. std::size_t is not designed to be portable, it's supposed to be the largest possible unsigned integer-type the system supports. std::size_t is entirely system dependent by design.

  • @mytech6779
    @mytech6779 8 หลายเดือนก่อน

    Most of the time address 0x00 is special and not used for normal allocation, thus it is overloaded as the null pointer address. There are exceptions though where derefrencing a pointer to 0x00 is a valid thing to do and not the same as trying to deref a null pointer. Really a lot of systems avoid the whole thing with special handling of 0x00 page in the virtual address mapping.
    (boilerplate disclaimer: If either special alignment or access to 0x00 really matters then double check the manuals for your system; hardware and OS)
    Though I can see how 0x00 being considered special could lead to assumptions that create an off by one alignment error.

  • @schmide
    @schmide 8 หลายเดือนก่อน +3

    Must is not correct. x86 is a byte addressable machine. There is no alignment restrictions on basic types. (char, short, int, float, double, long long). If they do cross their alignment, there is at most a load delay if the cpu crosses a cache line boundary. (64 bytes) In previous generations (sandy bridge and before) vector instructions (SSE/AVX) would fault if they were not aligned to a 128bit boundary, this has been relaxed since then and they will not fault as long as they are aligned to a dword. Regardless of all this it is good practice to align data. It matters on older hardware and ARM/RISC/GPU can often require greater alignment. As for what the user has to consider. For modern OSs, all heap allocations are cache aligned at 64bytes, stack primitives are often aligned as well, so if you are doing vector processing you need not declare alignment as you did before. As for manual ordering, it is best and easiest to put the largest types first, then the rest will fall into place. If you put smaller primitives first you have to group them to align with the latter elements or padding will be introduced.

  • @alpergunes6380
    @alpergunes6380 8 หลายเดือนก่อน +2

    I usually prefer to add explicit padding to the end of my structs to make the wasted space explicit and visible.
    struct S {
    char c;
    char _pad[3];
    int i;
    long long l;
    };

    • @AlfredoCorrea
      @AlfredoCorrea 8 หลายเดือนก่อน +3

      this is ok, but not if you care about aggregate initialization. perhaps named field initialization is a workaround now, I haven’t tried.

    • @alpergunes6380
      @alpergunes6380 8 หลายเดือนก่อน

      @@AlfredoCorrea true

    • @AlfredoCorrea
      @AlfredoCorrea 8 หลายเดือนก่อน

      @@alpergunes6380 Perhaps a hypothetical "hidden" attribute could solve this. `hidden: char _pad[3];`

  • @AtomicAndi
    @AtomicAndi 8 หลายเดือนก่อน

    visual studio has a new feature to visualize class layout
    apparently I'm not allowed to post links

    • @AtomicAndi
      @AtomicAndi 8 หลายเดือนก่อน

      oh, we are sponsored by CLion ;-)

    • @Minty_Meeo
      @Minty_Meeo 8 หลายเดือนก่อน +1

      TH-cam will block any comments with links to outside websites without exception. I'm not even sure if links to other TH-cam videos always make it through the filter.

    • @AtomicAndi
      @AtomicAndi 8 หลายเดือนก่อน

      @@Minty_Meeo I often comment with links to videos, so that's fine

  • @jasoncole7711
    @jasoncole7711 8 หลายเดือนก่อน

    Historically I've also sorted stack variables to be largest to smallest size; I don't know whether the compiler is smart enough to rearrange them if declared less than optimally.

    • @cppweekly
      @cppweekly  8 หลายเดือนก่อน +1

      On a modern compiler your stack objects are probably just residing in registers anyhow, or eliminated entirely.
      Your stack variables should be ordered in the way it logically makes sense to use and initialize them, and they should be limited in scope as much as possible usually.

  • @jeanclaudevanslamme
    @jeanclaudevanslamme 8 หลายเดือนก่อน

    Are you seriously suggesting that I, someone who is watching C++ educational content, might be making mistakes in C++? >:(

  • @Tyranisaur
    @Tyranisaur 8 หลายเดือนก่อน +21

    I think I'm using packed structs correctly, by not using it 😃

    • @ABaumstumpf
      @ABaumstumpf 8 หลายเดือนก่อน +1

      Same thing here :)

  • @Christosg8
    @Christosg8 8 หลายเดือนก่อน +9

    This was a nice opportunity to talk about bitfields as well and how the behave under alignment. We use them a lot at work (these 1-bit bitfields are a pain) and I've been bitten before due to alignment requirements of bitfields...

  • @not_ever
    @not_ever 8 หลายเดือนก่อน +3

    Clang tidy will also detect padding issues if you can’t use Wpadded. Apologies if this was said I’m in a noisy environment

  • @badassopenpolling
    @badassopenpolling 8 หลายเดือนก่อน +1

    Add data memebers in descending order of their size- then you will get optimal padding

  • @LeDabe
    @LeDabe 8 หลายเดือนก่อน +5

    A quick fix is simply to start the struct with the largest type and continue with the smallest type. If we assume every type is a power of two in size, you can prove that you will have the less padding this way.

    • @LeDabe
      @LeDabe 8 หลายเดือนก่อน

      so does sorting the values from smallest to largest AFAIK

    • @WndSks
      @WndSks 8 หลายเดือนก่อน +2

      X86 can access the first x bytes in the struct faster than later bytes, also sort by how important the member is. (I don't remember what x is, 256 perhaps)

    • @MirralisDias
      @MirralisDias 8 หลายเดือนก่อน

      ​@@LeDabeI was expecting him to place the long long first, then the ints, and then the chars. I can't test it now, but in this way would it be possible to get 19 bytes?

    • @loicballeydier5546
      @loicballeydier5546 8 หลายเดือนก่อน

      @@MirralisDias i think you have to be aligned on 8 bytes (64bit OS) so it will be also 24 bytes

    • @AlfredoCorrea
      @AlfredoCorrea 8 หลายเดือนก่อน

      it seems to me that the general rule is that you can do small to large or large to small or any “std::rotate” :) of that. no?

  • @LesleyLai
    @LesleyLai 8 หลายเดือนก่อน +3

    Anyone has an idea about why do you ever want to use std::alignment_of and use std::aligned_storage instead of alignof and alignas?

    • @friedkeenan
      @friedkeenan 8 หลายเดือนก่อน +1

      You don't want to use them, as I understand it. std::alignment_of was created before `alignof` existed, but now `alignof` supersedes it in all ways, though it still works fine. std::aligned_storage in fact can't alias for other objects like an alignas(T) std::byte[sizeof(T)] could, so using std::aligned_storage is probably in fact an error

    • @bryce.ferenczi
      @bryce.ferenczi 8 หลายเดือนก่อน +3

      You probabbly wouldn't want to use std::aligned_storage in greenfield code now since its depricated in cpp23+

    • @bryce.ferenczi
      @bryce.ferenczi 8 หลายเดือนก่อน +1

      You probabbly wouldn't want to use std::aligned_storage in greenfield code now since its depricated in cpp23+

  • @Omnifarious0
    @Omnifarious0 8 หลายเดือนก่อน +2

    I've always put the smallest ones at the end. I'm not sure which is better in general. If you have them in an array of course, the struct has to be padded out to the correct alignment for the most constrained element in it to be on the right boundary, and so I think that negates any advantage for ascending vs. descending size order.

  • @avramlevitter6150
    @avramlevitter6150 8 หลายเดือนก่อน +2

    This may seem like a minor issue, and it could be in a lot of projects (how many instances are you going to have of this particular struct?) but in some applications you might have millions of these you need to allocate and iterate, and in those the order of the fields is absolutely critical. And saving space isn't always better- due to the way memory access works, you might want to put members that are accessed together frequently next to each other even though it means the compiler injects a lot of padding.

  • @Денис-ь8ь7с
    @Денис-ь8ь7с 3 หลายเดือนก่อน

    I believe that when you arrange in largest-to-smallest way, you always get the best result. Because every smaller-to-larger pair causes padding and larger-to-smaller (or same-to-same) not. And end-of-the-struct padding with proper ordering consequently will be at least not bigger than total padding for any other member permutation case.

  • @nicholaskomsa1777
    @nicholaskomsa1777 4 หลายเดือนก่อน

    padding issues are solved largely by Structures of Arrays programming method, speed increase even more dramatic than worrying about complicated structure concepts.

  • @PGJVids
    @PGJVids 8 หลายเดือนก่อน +1

    Arranging the values from largest size to smallest size, then no padding would ever be used between values (64bit at the top, followed by 32bit, then 16bit, then 8bit). There could still be padding at the end when you have the structs in an array, to make sure the alignment of the next struct is correct.
    All this about alignment and padding is something i've had to deal with a bit while learning to use the Vulkan API, since the GPU doesn't handle it for you at all.

  • @tigrankato
    @tigrankato 8 หลายเดือนก่อน +2

    Is there any way to tell the compiler to automatically reorder struct members for the smallest possible size?

    • @ZHDINC
      @ZHDINC 8 หลายเดือนก่อน

      I'm curious about this as well. It seems like it'd be something trivial for the compiler to optimize. I mean, I certainly don't care if the compiler re-arranged the ordering of class members so they'll use less space.

    • @imtootired4122
      @imtootired4122 8 หลายเดือนก่อน +2

      C++23 requires that the order of the members in memory to match the order in the definition. Earlier standards were a bit looser and allowed members with different access control to be re-ordered.

    • @cppweekly
      @cppweekly  8 หลายเดือนก่อน +1

      @@imtootired4122 But no one did

  • @ysakhno
    @ysakhno 8 หลายเดือนก่อน

    You should have reversed the order of your fields in fact. As a rule of thumb, first should go all the long longs, then all ints, then all short ints (if you have them), then all chars/bytes/bools (if the size of bool is 1 byte). That is if you care about total size of your structs / performance so much. But in most cases this is largely irrelevant, in other words you should only care when you start to see problems.

  • @friedkeenan
    @friedkeenan 8 หลายเดือนก่อน +1

    Not only can unaligned access be slower, but it can also be simply disallowed by the hardware and cause a crash.

    • @Minty_Meeo
      @Minty_Meeo 8 หลายเดือนก่อน

      PowerPC had really great support for unaligned writes... except for the Wii which shipped with a hardware bug that corrupts unaligned writes to uncached memory! It's usually not worth the risk to invoke the terror of unaligned writes on any platform.

  • @FWindowsUseGaruda
    @FWindowsUseGaruda 8 หลายเดือนก่อน +1

    Thank you, this was an excellent explanation.

  • @caiovinicius7970
    @caiovinicius7970 4 หลายเดือนก่อน

    to avoid padded warnings you can introduce "char padding" members in your struct, to fill the gaps. this also allow for more easy knowing of were you can put new members in this struct wo breaking the alignment

  • @lysy-zn2gg
    @lysy-zn2gg 8 หลายเดือนก่อน

    I think you should only care in critical parts of code like Vertex structs in game development etc

  • @seanmacfoy5326
    @seanmacfoy5326 8 หลายเดือนก่อน

    Does the last char not start on byte 24?

  • @roblauer7568
    @roblauer7568 8 หลายเดือนก่อน

    I might be wrong, but ~5:54 in line 20 the comment says that the char is in byte 25. Isn't it 24?
    char a; // [0, 1) (1)
    // padding // [1, 4) (3)
    int i; // [4, 8) (4)
    char b; // [8, 9) (1)
    // padding // [9, 12) (3)
    int j; // [12, 16) (4)
    long long l; // [16, 24) (8)
    char c; // [24, 25) (1)

    • @cppweekly
      @cppweekly  8 หลายเดือนก่อน +1

      I probably did get something wrong, yeah.

    • @roblauer7568
      @roblauer7568 8 หลายเดือนก่อน

      @@cppweekly thanks for your reply. I’m shocked that you take the time :O
      Thank you for all the great videos! I learn so much from them.

    • @njagimwaniki4321
      @njagimwaniki4321 3 หลายเดือนก่อน

      @@roblauer7568 Why is the total byte usage here 32 and not 25? Is it because we have to store things in multiples of 8?

  • @mgeding
    @mgeding 8 หลายเดือนก่อน

    Absolutely bizarre that there was no mention of the `ailgnof` operator.

  • @rafi1082
    @rafi1082 8 หลายเดือนก่อน +1

    wrapping the struct with "#pragma pack(1)" will make it compile

    • @charlesolson9019
      @charlesolson9019 8 หลายเดือนก่อน +3

      That's a compiler-specific pragma, and I'm guessing it makes the code less efficient. I have done that in network protocol work, where you `reinterpret_cast()` a blob of data into a struct.

    • @bcodetube
      @bcodetube 8 หลายเดือนก่อน

      #pragma pack(push, 1)
      struct X {×××} ;
      #pragma pack(pop)
      This is also useful to read binary files that have well defined headers

  • @mateuslima5102
    @mateuslima5102 8 หลายเดือนก่อน

    i use packed structs to send data via serial protocol, is that correct?

    • @cppweekly
      @cppweekly  8 หลายเดือนก่อน +1

      You've probably got undefined behavior on the other end, if you're using reinterpret_cast or a c-style cast.

  • @emilkaczmarek4641
    @emilkaczmarek4641 8 หลายเดือนก่อน

    GCC 13.2 supports -Wpadded (cannot try earlier versions at the moment), but as warnings and is not as informative as clang.

    • @paulfee9839
      @paulfee9839 8 หลายเดือนก่อน

      Testing with compiler explorer, GCC supports -Wpadded at least back to version 4.1.2.

    • @ChristophePiault
      @ChristophePiault 8 หลายเดือนก่อน

      @@paulfee9839 It means that GCC supported -Wpadded before clang initial release. Pure free useless GCC bashing at 7:18.

  • @pandacongolais
    @pandacongolais 8 หลายเดือนก่อน +1

    The additional episodes list grows fast in the other comment.
    If not already done, please add _packing_ (+ endianess)
    I'm using this a lot in an embedded context. But I'm sure I'm missing some concepts, and you are certainly one of the most skilled to teach us !

    • @cppweekly
      @cppweekly  8 หลายเดือนก่อน +2

      It is, in fact, impossible to keep track of the topic requests in the video comments! I have a tracker here where you can add your request so it won't get lost - github.com/lefticus/cpp_weekly/issues/

    • @pandacongolais
      @pandacongolais 8 หลายเดือนก่อน

      @@cppweekly Done
      Maybe you should give this link in every video you publish, in the description or a pined comment.
      Anyway, a lot of thanks for your time and for making us less dumb !

  • @宋哲宇-p2n
    @宋哲宇-p2n 8 หลายเดือนก่อน +1

    I'm just curious about if the compiler can do the rearrangement for us automatically, since it's a seemingly simple optimization

    • @cppweekly
      @cppweekly  8 หลายเดือนก่อน +1

      Absolutely not, because it would break ABI compatibility

  • @onaecO
    @onaecO 8 หลายเดือนก่อน

    Any sources to really understand the deep stuff? Why this alignment gives optimization?

  • @milasudril
    @milasudril 8 หลายเดือนก่อน

    Isn't packed structs for interfacing with odd-sized binary formats? The question is then if you should save values to an unpacked version of the same struct when loading data, or keep it as is despite a potentially slower access to individual fields.

  • @dmitriidemenev5258
    @dmitriidemenev5258 8 หลายเดือนก่อน

    1:49 It's important to note that alignment of types is architecture dependent.
    7:03 Such behavior was inherited from C and isn't that great because the order of fields that is useful for humans isn't particularly good for the compiler. I find that the Rust's behavior is better because there the order of fields in the binary representation is decided by the compiler unless the programmer opts into doing this manually.
    There is also a very important overlooked topic - trailing padding with nested structs. It can highlight the importance of compile-time reflection and maybe you can demonstrate a workaround with boost/pfr.

  • @Yupppi
    @Yupppi 8 หลายเดือนก่อน

    Why doesn't compiler do anything about the padding? I would've intuitively set long long first, then ints and then chars.

  • @stephenhowe4107
    @stephenhowe4107 8 หลายเดือนก่อน

    I use packed structs but only for structs that are written to disk. If they are not, I use the default packing. Usually this is achieved via #pragma pack.

    • @oracleoftroy
      @oracleoftroy 7 หลายเดือนก่อน

      I would recommend not doing that. It is ok if you know your compiler, your platform, your cpu and don't need any sort of portability (the same computer will read and write the file) and are certain you won't run into alignment, packing or other issues no matter what happens in the future.
      Otherwise, I'd manually read/write the bytes in the proper order with the proper packing for the file. Something like:
      // write 32-bit little endian
      buf[0] = (value >> 0) && 0xFF;
      buf[1] = (value >> 8) && 0xFF;
      buf[2] = (value >> 16) && 0xFF;
      buf[3] = (value >> 24) && 0xFF;
      // read 32-bit little endian
      value = (buf[0]

  • @oldlonecoder5843
    @oldlonecoder5843 8 หลายเดือนก่อน

    Wtf? Pack will address exact struct representation of physical memory address for devices and physical address of IO ports, registers, flags....!!
    Nothing to do with c++!!!!!!!! Wtf? Pack is to get out of c++ normal addressing data in memory (natural integer and address boundaries padding)