Search code examples
memorycompiler-construction

How compilers rearranging variables optimizes the code?


So in general if i declare some variables in c and then printout their memory addresses, sometimes they would be out of order with the declaration. And my lecturer seem to imply that's done to optimize the code, however I don't see how this helps.

I suspect that the optimization comes from aligning the data to words as close as possible, but here I see two issues.

  • Rearrangement shouldn't help with alignment since there still would be same number of variables unaligned no matter how you rearrange them. If the alignment indeed what they going after, padding should be used.
  • If RAM is by definition "random access" why should alignment even matter? How is it underperforming if i read from byte 3 to byte 7 in a 4 byte word device?

Solution

  • I'll start with your second question: is memory really "random access". Memory access in modern hardware is quite complicated, so any simple answer is necessarily (over-)simplified, but stripped to its essence, it goes something like this: Memory is random access in the sense that any unit of memory can be read or written at any time, but only one unit at a time. A "unit" here is more than a byte; it might be more than a "word", but the important thing is that they are non-overlapping. So you can read any unit, but you can't read any sequence of bytes unless they are all in the same unit. If the part of the sequence of bytes is in one unit and the rest is in the next unit, yiu have to read both units. And since it is one at a time, that takes longer than reading a single unit.

    But that's still random access. It's not like a rotating disc, where you have to wait for the data you want to arrive at the read head. Or a tape drive, where you might need to wind or rewind the tape quite a distance before you get to the data you want.

    Once upon a time, the ability of a CPU to assemble a sequence of bytes was pretty limited. It could, for example, take the first two bytes in a four-byte word, or the last two bytes, but not the middle two bytes. If you needed the middle two bytes, you'd have to read all four bytes, and then shift and mask to get the bits you were interested in. These days, we know how to get a lot more circuitry onto a chip, and copying any consecutive bytes from a memory unit generally works. And the units themselves have gotten somewhat wider. So alignment is less important than it used to be. But it still matters, because reading a sequence that lives in two consecutive units still requires two accessed, one after the other.

    Also, not all CPUs are so flexible. On some CPUs, memory access to the middle of a word is not possible, so for those chips alignment is still very useful.

    Now, alignment is a pretty abstract word; the concrete implementation might have different alignments for different data sizes. Abd that's the model compilers were generally designed for. So if a two-byte datum could be at the beginningor the end of a four-byte unit, then its address must be even. But the four-byte unit must have an address divisible by four. So order does make a difference. Consider a datum consisting of two individual bytes, one two-byte "half-word", a a four-byte word. If those are arranged byte, halfword, byte, word, then that will take 12 bytes: a byte of data, a byte of padding, a half word (even alignment), another byte, three bytes of padding, and the word. If we arranged them byte, byte, half, word, then there's no padding and the data fits in eight bytes.