Data alignment in Eiffel

by Tao Feng (modified: 2014 Aug 27)

A process access memory in blocks. Thus the base address of data is important. Ideally all data should be aligned to an optimized size of block of memory dependent on different architectures, in order to gain max performance. On some platforms doing this is not only about performance but correctness. On platforms of different architectures, the size of block varies.


In Eiffel, Eiffel object is stored in a successive block of memory. Each object includes the header and the actual data for attributes. The structure of the header will be shown bellow which is always aligned on different platforms. The runtime allocates memory for each object according to the size of the header and keeps the size of the whole object mutiple of header's size.

Header structure /* Overhead for each memory segment allocated. All these objects * are linked by size when they are free. This link field is used * by Eiffel objects to store some flags. One bit is used to indicate * C objects, so the garbage collector will skip them. Another field * is used to store the size of the block. Only the lowest 27 bits are * used (thus limiting the size of an object to 2^27 bytes). The upper * 5 bits are used to store the status of the blocks. See below. */ union overhead { struct { union { union overhead *ovu_next; /* Next block with same size, 4 bytes (32bit), 8 bytes (64bit) */ char *ovu_fwd; /* Forwarding pointer, 4 bytes (32bit), 8 bytes (64bit) */ struct { EIF_TYPE_INDEX dtype; /* 2 bytes */ EIF_TYPE_INDEX dftype; /* 2 bytes */ uint16 flags; /* 2 bytes */ EIF_SCP_PID scp_pid; /* SCOOP Processor ID to which object belongs, 2 bytes*/ } ovs; } ovu; rt_uint_ptr ovs_size; /* 4 bytes (32bit), 8 bytes (64bit) */ #if (MEM_ALIGNBYTES == 8) && !defined(EIF_64_BITS) /* On 32-bit platform where alignment is 8, * we need to add an additional 4 bytes.*/ uint32 dummy; #endif #ifdef EIF_TID rt_uint_ptr ovs_tid; /* thread id of creator thread, for debug only */ #endif /* EIF_TID */ } ov_head; #if MEM_ALIGNBYTES > 8 double ov_padding; /* Alignment restrictions */ #endif };

The header structure above is either 12 bytes and 16 bytes depending on the platform. Most of the time it is 12 bytes when memory alignment is 4 on 32-bit architecture (e.g. x86), but otherwise 16 bytes. Talking about 64-bit, a 0-byte sized object always occupies 32 bytes of memory. Object sizes are therefore 32, 48, 64...

Other Consideration

Based on internals above, keep in mind that each Eiffel object might take larger space than it actually needs for the reason of memory alignment. Normally, one does not have to pay extra attention to data alignment when programming in Eiffel. But in some critical area, especially memory sensitive project that allocates tens of thousands of small objects, some attention to this would help getting most out of it. The basic priciple is that the total size of all attributes in a class should not exceed the border of multiple of the header size when not necessary.

In Eiffel parser, the abstract syntax tree is formed by a lot of small nodes, the size of each nodes is memory sensitive. We keep them as compact as possible. When new attributes are added, some calculation is done to ensure that it does not affect memory usage when not necessary. For example in class {ID_AS}, attributes are: index: INTEGER_32 internal_count: NATURAL_16 internal_location: NATURAL_32 name_id: INTEGER_32 position: INTEGER_32

64bit platform (bytes) 32bit platform (bytes)
index 4 4
internal_count 2 2
internal_location 4 4
name_id 4 4
position 4 4
Sum 18 18
Header size 16 12
Object size 16 * 3 = 48 12 * 3 = 36
Available 48 - 16 - 18 = 14 36 - 12 - 18 = 6

According to the table:

  • On 64-bit, we will have 14 bytes available for more data.
  • On 32-bit, we will have 6 bytes.

In above example, there are only expanded types. One needs to know that a reference type takes as many as 4 bytes.


  • In EiffelStudo, Object Internal (Object viewer) reports the physical object size of the viewing object.