Are arrays in .NET aligned to any boundary?
If yes, to which? And is it the same for all array types?
Data structure alignment is the way data is arranged and accessed in computer memory. Data alignment and Data structure padding are two different issues but are related to each other and together known as Data Structure alignment.
For instance, in a 32-bit architecture, the data may be aligned if the data is stored in four consecutive bytes and the first byte lies on a 4-byte boundary. Data alignment is the aligning of elements according to their natural alignment.
What is alignment? Alignment refers to the arrangement of data in memory, and specifically deals with the issue of accessing data as proper units of information from main memory. First we must conceptualize main memory as a contiguous block of consecutive memory locations. Each location contains a fixed number of bits.
Structure packing is only done when you tell your compiler explicitly to pack the structure. Padding is what you're seeing. Your 32-bit system is padding each field to word alignment. If you had told your compiler to pack the structures, they'd be 6 and 5 bytes, respectively.
The common language infrastructure (ECMA-335) places the following restrictions on alignment:
12.6.2 Alignment
Built-in data types shall be properly aligned, which is defined as follows:
- 1-byte, 2-byte, and 4-byte data is properly aligned when it is stored at a 1-byte, 2-byte, or 4-byte boundary, respectively.
- 8-byte data is properly aligned when it is stored on the same boundary required by the underlying hardware for atomic access to a native int.
Thus, int16 and unsigned int16 start on even address; int32, unsigned int32, and float32 start on an address divisible by 4; and int64, unsigned int64, and float64 start on an address divisible by 4 or 8, depending upon the target architecture. The native size types (native int, native unsigned int, and &) are always naturally aligned (4 bytes or 8 bytes, depending on the architecture). When generated externally, these should also be aligned to their natural size, although portable code can use 8-byte alignment to guarantee architecture independence. It is strongly recommended that float64 be aligned on an 8-byte boundary, even when the size of native int is 32 bits.
The CLI also specifies that you can use an unaligned
prefix to allow for abritrary alignment. Furthermore, the JIT must produce correct code to read and write regardless of the actual alignment.
Additionally, the CLI allows for the explicit layout of class fields:
explicitlayout
: A class markedexplicitlayout
causes the loader to ignore field sequence and to use the explicit layout rules provided, in the form of field offsets and/or overall class size or alignment. There are restrictions on valid layouts, specified in Partition II....
Optionally, a developer can specify a packing size for a class. This is layout information that is not often used, but it allows a developer to control the alignment of the fields. It is not an alignment specification, per se, but rather serves as a modifier that places a ceiling on all alignments. Typical values are 1, 2, 4, 8, or 16. Generic types shall not be marked
explicitlayout
.
I haven't done it myself, but if you need to control the alignment of an array for interoperability with non-managed mode, then you may consider using a (unsafe) fixed array inside a struct with the StructLayoutAttribute
applied, and see if that works.
In .NET objects (of which arrays are a species) are always aligned based on pointer size (e.g. 4 byte or 8 byte alignment). So, object pointers and object arrays are always aligned in .NET.
The code in Michael Graczyk's answer checks for alignment on the index, because although the array itself is aligned, since it's an Int32 array, the individual odd indices won't be aligned on 64 bit systems. On 32 bit systems, all indices of an Int32 array would be aligned.
So technically that method could be faster if it checked the process' bitness. On 32 bit processes, it wouldn't need to do the alignment check for Int32 arrays. Since all indices would be word aligned, and pointers are word length as well in that case.
I should also point out that dereferencing a pointer in .NET doesn't require alignment. However, it will be slower. e.g. if you have a valid byte* pointer and that points to data that is at least eight bytes in length, you can cast it to long* and get the value:
unsafe
{
var data = new byte[ 16 ];
fixed ( byte* dataP = data )
{
var misalignedlongP = ( long* ) ( dataP + 3 );
long value = *misalignedlongP;
}
}
Reading through .NET's source code, you can see that Microsoft sometimes accounts for alignment and often does not. An example would be the internal System.Buffer.Memmove
method (see https://referencesource.microsoft.com/#mscorlib/system/buffer.cs,c2ca91c0d34a8f86). That method has code paths that cast the byte* to long without any alignment checks in a few places, and the calling methods do not check alignment either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With