Why Do Some Types (E.G. Float80) Have a Memory Alignment Bigger Than Word Size

Why do some types (e.g. Float80) have a memory alignment bigger than word size?

With the help of Martin R's link and the hint that it is a processor design decision. I found the readon why.

Cache lines.

Cache lines are a very small memory for the processor, on the Intel Mac 64 bit of mine it is 128 bit (16 bytes).

As seen in the picture of the question I knew there was a difference between the dotted and the bold lines. The bold lines are between the cache lines of the processor. You don't want to load 2 cache lines if you could do better with a little more memory cost. So if the processor only allows, that types with a size of 8 bytes (or bigger) are aligned on the start of a cache line (every multiple of 16). There will be no two cache line reads for a type that is as big as a cache line (double the word size im my case, 16 bytes). As you can see in the picture only the red blocks are crossing the bold line (so they are not allowed per design).

See the link attached for more info.

Cache effects

SizeOf set of enums, 32 bit vs 64 bit and memory alignment

Here is my test program:

{$APPTYPE CONSOLE}

type
TEnumSet16 = set of 0..16-1;
TEnumSet17 = set of 0..17-1;
TEnumSet24 = set of 0..24-1;
TEnumSet25 = set of 0..25-1;
TEnumSet32 = set of 0..32-1;
TEnumSet33 = set of 0..33-1;
TEnumSet64 = set of 0..64-1;
TEnumSet65 = set of 0..65-1;

begin
Writeln(16, ':', SizeOf(TEnumSet16));
Writeln(17, ':', SizeOf(TEnumSet17));
Writeln(24, ':', SizeOf(TEnumSet24));
Writeln(25, ':', SizeOf(TEnumSet25));
Writeln(32, ':', SizeOf(TEnumSet32));
Writeln(33, ':', SizeOf(TEnumSet33));
Writeln(64, ':', SizeOf(TEnumSet64));
Writeln(65, ':', SizeOf(TEnumSet65));
end.

And the output (I am using XE7 but I expect that it is the same in all versions):











































32 bit64 bit
16:216:2
17:417:4
24:424:4
25:425:4
32:432:4
33:533:8
64:864:8
65:965:9

Strange data alignment

You can't make any definitive statements about addresses when you're allocating on the heap.

There's a good chance that the memory allocation functions maintain inline information about allocated blocks, which will affect the address of the following block, something like:

+--------+-------------+--------+-------------+
| header | alloced mem | header | alloced mem | ...
+--------+-------------+--------+-------------+

In addition, for efficiency, those functions may round up your memory to a multiple of (for example) eight or sixteen (you're still not allowed to use it, since you don't know about it). This may further affect the addresses you see for your allocated memory.


The classic case of both those effects conspiring against you can be seen with:

#include <iostream>
#include <cstdlib>
int main (int argc, char *argv[]) {
char *one = new char[std::atoi(argv[1])];
char *two = new char[std::atoi(argv[1])];
std::cout << static_cast<void*>(one) << '\n';
std::cout << static_cast<void*>(two) << '\n';
return 0;
}

and script:

#!/usr/bin/bash
for i in {01..37}; do
echo $i $(./qq $i)
done

On my system, this outputs:

01 0x800102e8 0x800102f8
02 0x800102e8 0x800102f8
:: (all the same address pairs in here and in gaps below)
12 0x800102e8 0x800102f8
13 0x800102e8 0x80010300
::
20 0x800102e8 0x80010300
21 0x800102e8 0x80010308
::
28 0x800102e8 0x80010308
29 0x800102e8 0x80010310
::
36 0x800102e8 0x80010310
37 0x800102e8 0x80010318

giving a whopping sixteen bytes between the two when you're only allocating a single character.

The fact that it remains sixteen bytes all the way up to new char[12] and increases by eight every time you add eight chars after that seems to indicate a four byte header, minimum sixteen bytes for the header+data, and an eight byte resolution on the header+data area.

Just keep in mind that's based on my knowledge of the way these things tend to be written so, while an educated guess, is still a guess, and you shouldn't rely on it. It may use a totally different strategy than what I think, or it may change its strategy for larger blocks.

If you want to know how much space the types really take, make an array of two and work out the difference in memory addresses between x[0] and x[1]. You'll find it should be the same as you get from sizeof.



Related Topics



Leave a reply



Submit