Understanding x86 vs ARM Memory Alignment on Android

Published:08/18/2011   Last Updated:08/18/2011

With Google’s recent release of the NDK (r6), it is now possible build Android application for x86 processors in addition to ARM. In general, this only involves rebuilding native code to port applications from ARM to x86. However, there are a few pitfalls to avoid.

One difference between x86 and ARM is the memory alignment requirements for data. Let’s look at a simple example:


This example just logs the size and offset of variables in TestStruct. The output for this program isn’t too surprising:

ARM
I/libtestjni( 5025): TestStruct (size: 12)
I/libtestjni( 5025): -- Var1 offset: 0
I/libtestjni( 5025): -- Var2 offset: 4
I/libtestjni( 5025): -- Var3 offset: 8

x86
I/libtestjni( 4175): TestStruct (size: 12)
I/libtestjni( 4175): -- Var1 offset: 0
I/libtestjni( 4175): -- Var2 offset: 4
I/libtestjni( 4175): -- Var3 offset: 8

But now, let’s change TestStruct to the following:


The output is now:

ARM
I/libtestjni( 4675): TestStruct (size: 24)
I/libtestjni( 4675): -- Var1 offset: 0
I/libtestjni( 4675): -- Var2 offset: 8
I/libtestjni( 4675): -- Var3 offset: 16

x86
I/libtestjni( 4079): TestStruct (size: 16)
I/libtestjni( 4079): -- Var1 offset: 0
I/libtestjni( 4079): -- Var2 offset: 4
I/libtestjni( 4079): -- Var3 offset: 12

The 8-byte (64-bit) mVar2 results in different layout for TestStruct. This is because ARM requires 8-byte alignment for 64-bit variables like mVar2. In most cases, this won’t cause problems because building for x86 vs ARM requires a full rebuild.

However, if an application serializes class or structures, this could cause a size mismatch. For example, say you create a save file on an ARM application and it writes TestStruct to a file. If you later load this file on an x86 platform, the class size in the application will be different than the saved file. As you can imagine, similar memory alignment issues can happen for network traffic that expects a specific memory layout.

The GCC compiler option “-malign-double” will generate the same memory alignment on x86 and ARM. However, since the OS was not built with this flag, it will break some OS calls.

You can control the alignment of variables through compiler attributes. So, if we tell GCC to align(8) for mVar2, x86 and ARM will have the same alignment:


The output is now:

ARM
I/libtestjni( 4675): TestStruct (size: 24)
I/libtestjni( 4675): -- Var1 offset: 0
I/libtestjni( 4675): -- Var2 offset: 8
I/libtestjni( 4675): -- Var3 offset: 16

x86
I/libtestjni( 4678): TestStruct (size: 24)
I/libtestjni( 4678): -- Var1 offset: 0
I/libtestjni( 4678): -- Var2 offset: 8
I/libtestjni( 4678): -- Var3 offset: 16

Once you understand the memory alignment difference between x86 and ARM, rebuilding your ARM Android NDK application for x86 should be pretty simple! Go grab the latest NDK and give it a try.

Product and Performance Information

1

Intel's compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel. Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice.

Notice revision #20110804