4
KeyValuePair<int, int>:    8 bytes
KeyValuePair<long, long>: 16 bytes
KeyValuePair<long, int>:  16 bytes (!!)
KeyValuePair<int, long>:  16 bytes (!!)

I would expect the latter two pairs to require only 8 (long) + 4 (int) = 12 bytes. Why do they take up 16?

Sizes were determined using dynamic SizeOf emitted through the ILGenerator, as discussed here: Size of generic structure

Community
  • 1
  • 1
Timo
  • 7,992
  • 4
  • 49
  • 67

1 Answers1

7

It's due to data structure Alignment. To quote the Wikipedia article:

Data alignment means putting the data at a memory address equal to some multiple of the word size, which increases the system's performance due to the way the CPU handles memory. To align the data, it may be necessary to insert some meaningless bytes between the end of the last data structure and the start of the next, which is data structure padding.

As an int is 4 bytes having a KeyValuePair of a long and an int would result in misaligned data structures. This would result in a significant drop in performance. It's therefore better to "waste" 4 bytes on each record to get the data aligned and therefore able to be written and read efficiently.

As to why single ints aren't padded - don't forget with a structure/class it's relatively easy to add padding variable - but for a plain value type you haven't got that structure/class there. I'd guess that the extra work required (adding a dummy structure around the int to insert the padding into) isn't worth the performance gain.

There's real no difference between 32 bit and 64 bit operating systems here, they'll still need padding and aligning, but the amount of padding might vary.

In the "old days" you often had to manually add padding to data structures to get the correct alignment, these days it tends to be handled automatically by the compiler.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
  • I was expecting something along these lines. But I also note that a common int does not get any padding, even though it is not a multiple of 8 bytes. Is it acceptable for it to be misaligned? Is it a trade-off? "We'll have so many ints that we want take the performance hit to save memory, but KeyValuePair<> will be rare enough that we'd rather align it correctly." Or am I missing something? – Timo Feb 02 '16 at 14:05
  • Also, does 32-bit vs 64-bit matter here? The sizes would suggest not. – Timo Feb 02 '16 at 14:05
  • @Timo no, 32 bit vs 64 bit doesn't matter. – ChrisF Feb 02 '16 at 14:06
  • This doesn't hold up as an explanation for why `` fits into 8 bytes, but does inadvertently suggest why `` isn't sized at 12 bytes. You've "got [a] structure/class there" to exactly the same extent with `` as with ``. Misalignment is "bad" when it causes values to _span_ words; storing 2 `int`s in 1 word has no fetch penalty, hence the 8-byte ``. Having a 12-byte struct, on the other hand, _could_ cause the _next_ adjacent value in memory to span words. It appears that CIL avoids this by rounding struct sizes up to the nearest word. – M-Pixel Jan 03 '22 at 05:08