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

Поделиться
HTML-код
  • Опубликовано: 2 июл 2024
  • ☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
    Upcoming Workshop: Understanding Object Lifetime, C++ On Sea, July 2, 2024
    ► cpponsea.uk/2024/sessions/und...
    Upcoming Workshop: C++ Best Practices, NDC TechTown, Sept 9-10, 2024
    ► ndctechtown.com/workshops/c-b...
    Upcoming Workshop: Applied constexpr: The Power of Compile-Time Resources, C++ Under The Sea, October 10, 2024
    ► cppunderthesea.nl/workshops/
    Besides yourself, who knows your project best? Your IDE! This is what makes AI Assistant in CLion, a C++ IDE by JetBrains, so context-aware and helpful.
    * Wondering how to adopt C++ modules into your project or link to a selected library in CMake? Send your question to the AI chat. Its answer will take into account the technology you use and your project context.
    * Need to research an error? AI Assistant in CLion can explain a runtime error, a CMake execution failure, or a selected piece of code.
    * Want to improve your code? AI Assistant will suggest a refactoring and generate documentation for you.
    JetBrains AI Assistant is now generally available. Use AI Assistant in CLion as an add-on with a JetBrains AI service subscription.
    Learn more at jb.gg/cpp_ai
    Episode details: github.com/lefticus/cpp_weekl...
    T-SHIRTS AVAILABLE!
    ► The best C++ T-Shirts anywhere! my-store-d16a2f.creator-sprin...
    WANT MORE JASON?
    ► My Training Classes: emptycrate.com/training.html
    ► Follow me on twitter: / lefticus
    SUPPORT THE CHANNEL
    ► Patreon: / lefticus
    ► Github Sponsors: github.com/sponsors/lefticus
    ► Paypal Donation: www.paypal.com/donate/?hosted...
    GET INVOLVED
    ► Video Idea List: github.com/lefticus/cpp_weekl...
    JASON'S BOOKS
    ► C++23 Best Practices
    Leanpub Ebook: leanpub.com/cpp23_best_practi...
    ► C++ Best Practices
    Amazon Paperback: amzn.to/3wpAU3Z
    Leanpub Ebook: leanpub.com/cppbestpractices
    JASON'S PUZZLE BOOKS
    ► Object Lifetime Puzzlers Book 1
    Amazon Paperback: amzn.to/3g6Ervj
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 2
    Amazon Paperback: amzn.to/3whdUDU
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 3
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Copy and Reference Puzzlers Book 1
    Amazon Paperback: amzn.to/3g7ZVb9
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 2
    Amazon Paperback: amzn.to/3X1LOIx
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 3
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► OpCode Puzzlers Book 1
    Amazon Paperback: amzn.to/3KCNJg6
    Leanpub Ebook: leanpub.com/opcodepuzzlers_book1
    RECOMMENDED BOOKS
    ► Bjarne Stroustrup's A Tour of C++ (now with C++20/23!): amzn.to/3X4Wypr
    AWESOME PROJECTS
    ► The C++ Starter Project - Gets you started with Best Practices Quickly - github.com/cpp-best-practices...
    ► C++ Best Practices Forkable Coding Standards - github.com/cpp-best-practices...
    O'Reilly VIDEOS
    ► Inheritance and Polymorphism in C++ - www.oreilly.com/library/view/...
    ► Learning C++ Best Practices - www.oreilly.com/library/view/...
  • НаукаНаука

Комментарии • 88

  • @kuhluhOG
    @kuhluhOG 5 месяцев назад +45

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

    • @Minty_Meeo
      @Minty_Meeo 5 месяцев назад +15

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

  • @victotronics
    @victotronics 5 месяцев назад +90

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

    • @alexvergara2491
      @alexvergara2491 5 месяцев назад +2

      BEST comment and idea

    • @dmitriidemenev5258
      @dmitriidemenev5258 5 месяцев назад +9

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

  • @stephenhowe4107
    @stephenhowe4107 5 месяцев назад +26

    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 5 месяцев назад +1

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

  • @Christosg8
    @Christosg8 5 месяцев назад +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...

  • @caiovinicius7970
    @caiovinicius7970 Месяц назад

    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

  • @snbv5real
    @snbv5real 5 месяцев назад +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.

  • @user-wl1sn8qr5f
    @user-wl1sn8qr5f 25 дней назад

    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.

  • @Tyranisaur
    @Tyranisaur 5 месяцев назад +21

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

    • @ABaumstumpf
      @ABaumstumpf 5 месяцев назад +1

      Same thing here :)

  • @bernardmayeur3952
    @bernardmayeur3952 5 месяцев назад +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 5 месяцев назад

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

    • @TheRobbix1206
      @TheRobbix1206 5 месяцев назад +3

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

  • @danielkatzav9971
    @danielkatzav9971 5 месяцев назад +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.

  • @LeDabe
    @LeDabe 5 месяцев назад +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 5 месяцев назад

      so does sorting the values from smallest to largest AFAIK

    • @WndSks
      @WndSks 5 месяцев назад +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 5 месяцев назад

      ​@@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 5 месяцев назад

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

    • @AlfredoCorrea
      @AlfredoCorrea 5 месяцев назад

      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?

  • @not_ever
    @not_ever 5 месяцев назад +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

  • @FWindowsUseGaruda
    @FWindowsUseGaruda 5 месяцев назад +1

    Thank you, this was an excellent explanation.

  • @anon_y_mousse
    @anon_y_mousse 4 месяца назад

    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.

  • @Omnifarious0
    @Omnifarious0 5 месяцев назад +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.

  • @badassopenpolling
    @badassopenpolling 5 месяцев назад +1

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

  • @avramlevitter6150
    @avramlevitter6150 5 месяцев назад +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.

  • @PGJVids
    @PGJVids 5 месяцев назад +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.

  • @nicholaskomsa1777
    @nicholaskomsa1777 Месяц назад

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

  • @seanmacfoy5326
    @seanmacfoy5326 5 месяцев назад

    Does the last char not start on byte 24?

  • @lysy-zn2gg
    @lysy-zn2gg 5 месяцев назад

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

  • @friedkeenan
    @friedkeenan 5 месяцев назад +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 5 месяцев назад

      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.

  • @tigrankato
    @tigrankato 5 месяцев назад +1

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

    • @ZHDINC
      @ZHDINC 5 месяцев назад

      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 5 месяцев назад +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  5 месяцев назад +1

      @@imtootired4122 But no one did

  • @LesleyLai
    @LesleyLai 5 месяцев назад +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 5 месяцев назад +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 5 месяцев назад +3

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

    • @bryce.ferenczi
      @bryce.ferenczi 5 месяцев назад +1

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

  • @mytech6779
    @mytech6779 5 месяцев назад

    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.

  • @pandacongolais
    @pandacongolais 5 месяцев назад +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  5 месяцев назад +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 5 месяцев назад

      @@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 !

  • @milasudril
    @milasudril 5 месяцев назад

    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.

  • @user-wu4ih6kx8t
    @user-wu4ih6kx8t 5 месяцев назад +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  5 месяцев назад +1

      Absolutely not, because it would break ABI compatibility

  • @alpergunes6380
    @alpergunes6380 5 месяцев назад +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 5 месяцев назад +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 5 месяцев назад

      @@AlfredoCorrea true

    • @AlfredoCorrea
      @AlfredoCorrea 5 месяцев назад

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

  • @nicholaskomsa1777
    @nicholaskomsa1777 Месяц назад

    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  Месяц назад

      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 Месяц назад

      @@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.

  • @onaecO
    @onaecO 5 месяцев назад

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

  • @roblauer7568
    @roblauer7568 5 месяцев назад

    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  5 месяцев назад +1

      I probably did get something wrong, yeah.

    • @roblauer7568
      @roblauer7568 5 месяцев назад

      @@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 29 дней назад

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

  • @AtomicAndi
    @AtomicAndi 5 месяцев назад

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

    • @AtomicAndi
      @AtomicAndi 5 месяцев назад

      oh, we are sponsored by CLion ;-)

    • @Minty_Meeo
      @Minty_Meeo 5 месяцев назад +1

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

    • @AtomicAndi
      @AtomicAndi 5 месяцев назад

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

  • @ysakhno
    @ysakhno 5 месяцев назад

    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.

  • @stephenhowe4107
    @stephenhowe4107 5 месяцев назад

    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 5 месяцев назад

      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]

  • @jasoncole7711
    @jasoncole7711 5 месяцев назад

    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  5 месяцев назад +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.

  • @mateuslima5102
    @mateuslima5102 5 месяцев назад

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

    • @cppweekly
      @cppweekly  5 месяцев назад +1

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

  • @schmide
    @schmide 5 месяцев назад +2

    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.

  • @dmitriidemenev5258
    @dmitriidemenev5258 5 месяцев назад

    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.

  • @rafi1082
    @rafi1082 5 месяцев назад +1

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

    • @charlesolson9019
      @charlesolson9019 5 месяцев назад +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 5 месяцев назад

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

  • @Yupppi
    @Yupppi 5 месяцев назад

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

  • @emilkaczmarek4641
    @emilkaczmarek4641 5 месяцев назад

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

    • @paulfee9839
      @paulfee9839 5 месяцев назад

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

    • @ChristophePiault
      @ChristophePiault 5 месяцев назад

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

  • @mgeding
    @mgeding 5 месяцев назад

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

  • @jeanclaudevanslamme
    @jeanclaudevanslamme 5 месяцев назад

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

  • @oldlonecoder5843
    @oldlonecoder5843 5 месяцев назад

    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)