The Lost Art of C Structure Packing
catb.org
catb.org
The reasons you would do it today are different than a decade ago and the rules have changed because the processors have changed. To add two clarifying points to the original article:
- The main reason to do optimal structure packing today is to reduce cache line misses. Because cache line misses are so expensive it is a big net performance gain in many cases to have the code do a little more work if it reduces cache line fills; optimal structure packing is basically a "free" way of minimizing cache misses.
- On modern Intel microarchitectures, alignment matters much less for performance than it used to. One of the big changes starting with the i7 is that unaligned memory accesses have approximately the same cost as aligned memory accesses. This is a pretty radical change to the optimization assumptions for structure layout. Consequently, it is possible to do very tight memory packing without the severe performance penalty traditionally implied.
What constitutes "optimal" structure packing is architecture dependent. The original C structure rules were designed in part to allow the structures to be portable above all else. If you design highly optimized structures for a Haswell processor, code may run much more slowly or create a CPU exception and crash on other architectures, so keep these tradeoffs in mind. The article is discussing basic structure packing which typically has easily predictable behavior almost anywhere C compiles.
About the only safe usage of structure memory layout that I can think of is offsetof, and even then, I think you could make offsetof's behaviour definable in the ABI to make it deterministic.
Spoken like someone who's never been forced to fix such code left behind by previous coders.
> About the only safe usage of structure memory layout that I can think of is offsetof, and even then, I think you could make offsetof's behaviour definable in the ABI to make it deterministic.
Sure, especially considering offsetof is standard defined and allowed to be compiler implemented AFAIK. That said, you bring up a great point: You're talking about breaking decades old ABIs if you were to enable such memory layout reordering by default. Good luck tracking down updated .libs or original source to build .libs from for every single one of your dependencies!
I think sequentially allocated states that fact. From what information do you mean it can be deduced?
ISO C actually explicitly specifies that the order is same in §6.7.2.1.13.
The "deduced" remark was essentially intended to cover constraints that come from 6.2.6.1.2 ("...objects are composed of contiguous sequences of one or more bytes..."), ie. fields cannot be (conceptually) outside &foo and ((char*)&foo)+sizeof(foo), which can be hard to implement on sufficiently weird architectures.
struct {
char a;
char b;
char c;
char *p;
char d;
}
A valgrind error that points at "0 bytes within an area of memory..." would signal an error originating somewhere in the struct, but who knows whether it's p or a.Traditionally, C and C++ programs have been compiled by running the compiler on each .c file, and then linking together the results. If each invocation of the compiler could put the fields of a structure in arbitrary order, the different object files would not work together correctly. C structures do not include runtime type information and all offsets are calculated during the compile phase. The same issues come up with libraries. We need a stable application binary interface, or ABI.
Even if the ABI problem was solved, there are advantages to letting the programmer determine the order of fields in a structure. If you know that different threads are going to be using different parts of a structure, you will want to arrange things (or insert padding) so that the different threads are using different cache lines. This avoids so-called "false-sharing."
That just requires that your packing algorithm be deterministic and there's no reason for it not to be.
I suppose it would fail in cases where someone defines a 2 element struct in one file that's a subset of a 3 element struct in another file, and then casts the 3 element into the 2 element.
static_assert(sizeof(foo)==64, "you didn't align foo correctly") would not be possible.
That is why I am familiar with struct packing actually, for creating protocols it is essential each end has the structure defined and laid out in the exact same way.
Example: https://groups.google.com/d/msg/erlang-programming/s39IQlk-d...
I wished more programming languages had baked in support for bitfields, bit arrays and endian conversion, because building interoperable, efficient binary clients without icky code generation is currently a PITA.
The packing algorithm would also need to be static, since object code produced by different compilers, or by different compiler settings (eg. -O2 or -O3) is still expected to link together correctly without error.
Some kind of attribute declaration in the struct definition itself could manage this, assuming that the algorithm it specifies is deterministic. But it can't be completely automatic, since that would differ from the current (deterministic) algorithm.
its more complicated than just keeping the size down though - you might want things to start on 64 or 128 or 256-bit boundaries as well - e.g. if your class contains aligned vector members and is itself aligned.
When I first learned to program in the 80s, this was known as "Fortran order" (as opposed to Pascal or C order), and in the later nineties, I think that "column major" and "row major" became popular, and they still are.
This goes back to sometimes in the 60's, with APL and Fortran doing column major on purpose, BASIC by accident, and most of the rest doing Row major.
It was only a wart because they didn't support compound types at all (and in some cases they did support compound types but people were not taught them when moving to a better language and kept their old techniques). It is compounded by the fact most scripting languages are untyped, so structure packing (the main justification for using arrays like that in more modern languages (for general memory use, stack size, or cache-hit optimisations)) isn't nearly as relevant.
Basically if you see parallel arrays used without a comment stating that it is done for packing reasons or similar, it is there either because the language does not support compound structures well or because the programmer knew no better. In a language where structure packing can be useful, it is a perfectly valid optimisation (though for small data not in tight loops, it may be an optimisation too far).
It's a modern ISA, implemented by modern microarchitectures.
So yes, chips that can only do aligned memory access do tend to be older.
Listen, I know that there are people who worked into retirement without ever seeing anything other than x86 but a) this shows how old x86 is and b) does not give them expertise over different ISAs.
MIPS is still in the top 4 or so CPU architectures by volume, with x86, PPC, and ARM, with ARM as by far the largest (forecast about 3 billion cores licensed last year).
At least a few hundred million MIPS cores are shipped every year. Depending on what you think embedded x86 volume is, it may be ahead of x86. Of course by revenue x86 still beats everyone.
You are not an advanced C programmer until you have grasped it. You are not a master of C until you could have written this document yourself and can criticize it intelligently
In other words, a "hacker" is someone just like ESR. More blowhardery. I for one don't claim to be a master...
i.e., 'a hacker is someone who looks like me'.
That's just one particularly egregious example. There are a gazillion others. Pretty much everything the guy has ever written has similar implications, where he defines a hacker as being whatever he sees himself as at the time. He really really wants to believe that there is a very specific hacker subculture (in his words, "our tribe"), and that they have a shared culture, heritage, and beliefs about things, and that they have some sort of meritocracy where certain members of that subculture are universally agreed upon as being wise and correct about everything (in his words, "the elders of our tribe"), and of course, that he is one of those people at the top of the imaginary meritocracy in this imaginary subculture.
Also, for bonus douchebaggery points, as if he needed them: http://esr.ibiblio.org/?p=208
The current entry:
> Formerly vaguely liberal-moderate, more recently moderate-to-neoconservative (hackers too were affected by the collapse of socialism). There is a strong libertarian contingent which rejects conventional left-right politics entirely. The only safe generalization is that hackers tend to be rather anti-authoritarian; thus, both paleoconservatism and ‘hard’ leftism are rare. Hackers are far more likely than most non-hackers to either (a) be aggressively apolitical or (b) entertain peculiar or idiosyncratic political ideas and actually try to live by them day-to-day.
archive.org says this was the same in 2003. (Possibly it changed and changed back, but I assume not.)
From march 2000 (v4.2.2 at http://jargon-file.org/archive/ which I selected somewhat at random, I didn't try to find the earliest "politics" entry):
> Vaguely liberal-moderate, except for the strong libertarian contingent which rejects conventional left-right politics entirely. The only safe generalization is that hackers tend to be rather anti-authoritarian; thus, both conventional conservatism and `hard' leftism are rare. Hackers are far more likely than most non-hackers to either (a) be aggressively apolitical or (b) entertain peculiar or idiosyncratic political ideas and actually try to live by them day-to-day.
ESR in 2008 ( http://esr.ibiblio.org/?p=301 ):
> I am not and have never been a conservative. Much less a “neocon”, whatever that means.
"Hackers tend to be libertarian" and "hackers tend to be neocons" are not sentiments expressed by either variant. "He decided that he was a neocon" just seems to be plain false. Whatever the merits of the changes he made, I claim that you are being uncharitable towards him.
I'm not necessarily defending ESR himself, I just think that you're attacking someone who isn't ESR and calling them ESR. And I think this is a common theme when people talk about ESR.
After 2001 it was "formerly liberal-moderate... moderate-to-neoconservative", when ESR had gone full-on warblogger. Around the same time he also added heavily politically biased definitions for terms like "fisking" and "idiotarian" to the Jargon File.
He claimed not to be a neocon in 2008 when "neocon" had become a dirty word. I don't think that changes the fact that he was a fervent supporter of the "War on Terrorism", a neocon project.
It sounds to me like you're agreeing with the parent -- he changed the description of a "hacker's" politics to fit whatever his particular political stance was at the time.
I agree he should stop trying to make "idiotarian" happen and it's completely out of place in the File.
I'm pretty sure ESR identified as a libertarian when he made this change. Why did you delete the "... strong libertarian contingent... anti-authoritarian" from this bit?
> I don't think that changes the fact that he was a fervent supporter of the "War on Terrorism", a neocon project.
The parent said he identified as a neocon. Admittedly, I don't know why he would use the word in 2001 if he didn't know what it meant in 2008. But I don't think it's true. I don't think there is any time (within relevant history, probably ever) when ESR thought he was a neoconservative, even if other people would call him one.
> he changed the description of a "hacker's" politics to fit whatever his particular political stance was at the time.
I'm not saying he didn't do this, but I think it's much less obvious that he did this than the parent (and yourself, to a lesser extent) made out. The parent said that when he identified as a libertarian, the jargon file said that hackers tended to be libertarian (which it didn't); and that when he identified as a neoconservative (which he never has) it changed to say that hackers tended to be neoconservative (which it doesn't).
If ESR is actually doing bad things to the jargon file, we should be able to point at them instead of telling falsehoods. Maybe the "liberal-moderate" to "moderate-neoconservative" change was in fact a bad one. I do consider it plausible that it was a politically-motivated change with no basis in reality. But if so, we should say things like "I don't think there's any evidence that hacker politics actually changed in the relevant time period", or better yet, "I have evidence that they didn't". Instead we have things like "ESR decided he was a neocon and updated the jargon file to say hackers tend to be neocons", which is a great soundbite if you want to make him look bad, but it's not true.
(I'm not defending "fisk" and "idiotarian". But the parent didn't mention them. The parent made one specific accusation about ESR, which was not true, and I pointed out that it was not true. The fact that what the parent said, and what actually happened, can be interpreted in the same light, does not mean that I agree with the parent.)
I don't think ESR's evaluation of what a "hacker" is means anything, so I don't think there's any measurable way to say whether "hacker politics" changed. That's the point: ESR identifies a "hacker" as someone like himself, and that definition changes with him.
My general claim here is "people treat ESR uncharitably". In this thread, that started with people choosing an unfavourable interpretation of you are not a master of C until.... When I pointed out that this was uncharitable, people said "this is a common theme in ESR's writing". I think that what they meant was something like "it's okay to take the uncharitable interpretation, because ESR often writes things similar to the uncharitable interpretation, so it's probably correct".
mwfunk gave a specific example of ESR acting similar to the uncharitable interpretation. I pointed out that the example was simply not true. It made three verifiable claims, and all of them were false. I call mwfunk uncharitable for this.
The example was "ESR took action Y". You said my evidence showed that ESR actually took action Z, and Y and Z are both instances of ESR taking meta-action A (updating the jargon file to say hacker politics match his own). And if ESR has done A, then uncharity becomes more justified.
But to interpret Z as A is much easier if we accuse ESR of being a neoconservative. This, too, is uncharitable: he has said that he is not and has never been a conservative, and that conservatives are villains. He did align with neoconservatives on one issue that a lot of people fervently disagreed with neoconservatives about, but that doesn't make him a neoconservative. And if ESR isn't a neoconservative, and merely aligns with them on one issue that people care a lot about, then Z is much less strongly an instance of A.
You have leveled accusations against ESR that I think stand by themselves. But they're far less damning than we started with, and in the meantime, this thread has been filled with examples of uncharity, which is exactly what I'm objecting to.
(On a broad level, I think what we have here is uncharity piled upon uncharity. ESR says something, and people interpret it uncharitably, because the uncharitable interpretation is how ESR acts; and they know this, because of uncharitable interpretations of previous things that he's done. But those interpretations are justified because...
And perhaps this tower bottoms out with something that is not uncharitable, but every level of uncharity makes the next one less justifiable.)
I think ESR's claims not to be a "neoconservative" stink of protesting too much. What he identifies as being at any given time doesn't mean a whole lot to me -- people can call themselves whatever they want. After seeing him dance and dissemble around his support of "race realism" for years I think it is overly charitable to take anything he says about his political categorization at face value.
If it helps, look at the other end of his change: he changed the description to say "formerly liberal" at the same time he was actively jumping into the fray as a "warblogger" and "anti-idiotarian". If he didn't change the definition to explicitly include his own politics (I believe he did) at the very least he changed it to exclude his political opponents.
All of the changes we've talked about here follow one pattern: altering work under his control to say that a "hacker" (which ESR believes to be a title of honor) is someone who resembles him more and resembles people he dislikes less. His commentary in the linked article follows the same pattern of self-aggrandizement by identification with an idealized and lionized hacker.
This is a lot more than I had hoped to ever write about ESR. All that said, if you told me I had to have dinner with him, Linus, or RMS, I'd choose ESR in a heartbeat. He also is a pretty good writer and evangelist and seems to be an effective organizer.
That's longer than I was expecting! I think 2014 is going to be a good year!
Is he? I'm not trying to be insulting, but I honestly can't think of anything that he's done that I might be impressed by.
That's a guess though, no numbers. :)
The struct reorg and matrix reorg optimizations (command-line options -fipa-struct-reorg and -fipa-matrix-reorg) have been removed. They did not always work correctly, nor did they work with link-time optimization (LTO), hence were only applicable to programs consisting of a single translation unit.
* http://en.wikipedia.org/wiki/CPU_cache#Cache_entries
* http://stackoverflow.com/questions/14707803/line-size-of-l1-...
struct TCP_Packet {
uint16_t source_port;
uint16_t dest_port;
uint32_t seq_no;
uint16_t flags;
uint16_t window_size;
uint16_t checksum;
unit16_t urgent;
...
} __attribute__ ((packed));> The simplest way to eliminate slop is to reorder the structure members by decreasing alignment. That is: make all the pointer-aligned subfields come first, because on a 64-bit machine they will be 8 bytes. Then the 4-byte ints; then the 2-byte shorts; then the character fields.
I wrote here [1] about using it in a KDE program (KStars) to cut 10% off the memory usage. In particular you can also use this tool to see some of the many bad effects of deeply nested classes: your class may be 8-byte aligned, so a 'char' at the end can take up eight bytes with padding, and it's not possible (AFAIK) to rearrange fields up and down inheritance heirarchies [2].
[1]: http://www.hdevalence.ca/blog/2013-12-22-better-living-throu...
[2]: C++ is not my favorite programming language.
#pragma pack(1)
MSVC
http://gcc.gnu.org/onlinedocs/gcc/Structure-Packing-Pragmas....
Clang has this, too.
struct SomeCamelCaseNonsenseStructure {
uint8_t flag;
uint32_t pointer;
uint8_t another_flag;
uint32_t another_pointer;
...
}
I feel like flinching every time I read it.small remark: this will cover dates to 2050
that's a bit of a gamble, not sure I would have done that myself. It's probably ok but you never know in 40 years CVS and your code is still in use somewhere and you'll cause some people headaches :]
pub struct stat {
st_dev: dev_t,
__pad1: c_short,
st_ino: ino_t,
st_mode: mode_t,
st_nlink: nlink_t,
st_uid: uid_t,
st_gid: gid_t,
st_rdev: dev_t,
__pad2: c_short,
st_size: off_t,
Note the __pad elements.[1] https://github.com/mozilla/rust/blob/master/src/libstd/libc....
No, a compiler can't do this. You have to assume that the first member of a struct will be machine word aligned. I'm sure there are many reasons, but the one I can think of now is that structs can be dynamically allocated. That means it has to be possible to take the return value of malloc() and assign it to a pointer, and there is no way to get malloc() to return a memory block with that wired alignment (starting on the last byte of a machine word).
Well that's disappointing. Is there any [nonstandard] way to get a compiler to take advantage of that space?
For the rest I blindly trust the compiler to choose optimal boundaries and alignments.
http://jheriko-rtw.blogspot.co.uk/2011/02/know-your-cc-struc...
I seem to remember it being mentioned in Bruce Eckel's excellent "Thinking in C++".
That said, I'm kinda curious how much this affects Objective C objects.