了解 Android* 上的 x86 与 ARM* 内存对齐的异同

提交新文章

2011年11月08日 07:00


谷歌最新的 NDK 版本 (r6) 除了支持为 ARM 构建应用外,还支持为x86 处理器构建 Android* 应用。一般而言,这只需重新构建原生代码,将应用从 ARM 移植到 x86即可。然而,其中也有一些错误需要避免。

x86 与 ARM 之间的差别之一在于数据的内存对齐要求。请看一个简单例子:



以上例子只记录了 TestStruct 中各变量的大小和偏移。这个程序的输出并不令人惊奇:

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

现在,让我们将 TestStruct 更改为以下内容:



输出如下:

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

8 字节(64位)mVar2 导致了不同的 TestStruct 布局。这是因为 ARM 需要 对像mVar这样的64 位变量进行 8 字节对齐。在大多数情况下,这不会导致问题,因为相较于 ARM,构建x86 应用时需要完全重新构建。

然而,如果应用对类或结构执行了序列化,则会导致大小不匹配。例如,如果您在 ARM 应用上创建了一个 save 文件,并且应用向文件中写入了 TestStruct。如果您之后在 x86 平台上加载该文件,则应用中的类大小将与保存的文件中的类大小不同。正如您所想象的,相似的内存对齐问题也会在需要特定内存布局的网络流量上发生。

GCC 编译器选项“-malign-double”将在 x86 和 ARM 上实现相同的内存对齐。然而,由于操作系统并非使用此标志创建,因此这会中断一些操作系统调用。

您可以通过编译器属性来控制不同变量的对齐。因此,如果我们告知 GCC 针对 mVar2 运行 align(8),x86 和 ARM 将具有相同的对齐结果:



输出如下:

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

了解了 x86 与 ARM 之间的内存对齐差异之后,重新构建面向 x86 的 Android* NDK 应用将非常简单。立即获取最新的 NDK 并进行尝试吧。


注:本文转引自英文博文Understanding x86 vs ARM Memory Alignment on Android,欲阅读英文原文,请点击该链接。