【问题标题】:Producing optimised NDK code for multiple architectures?为多种架构生成优化的 NDK 代码?
【发布时间】:2011-07-02 15:58:21
【问题描述】:

我有一些适用于 Android 的 C 代码,用于处理大量低级数字运算。我想知道我应该使用哪些设置(例如,对于我的 Android.mk 和 Application.mk)文件,以便生成的代码可以在所有当前的 Android 设备上运行,而且还可以利用针对特定芯片组的优化。我正在寻找好的默认 Android.mk 和 Application.mk 设置来使用,并且我想避免在我的 C 代码中乱扔#ifdef 分支。

例如,我知道 ARMv7 具有浮点指令,并且一些 ARMv7 芯片支持 NEON 指令,而默认的 ARM 都不支持这些指令。是否可以设置标志以便我可以使用 NEON 构建 ARMv7、没有 NEON 的 ARMv7 和默认的 ARM 构建?我知道如何做后两个,但不是全部 3。我对我使用的设置持谨慎态度,因为我认为当前的默认设置是最安全的设置以及其他选项有什么风险。

对于 GCC 特定的优化,我使用以下标志:

LOCAL_CFLAGS=-ffast-math -O3 -funroll-loops

我已经检查了所有这 3 个可以加快我的代码的速度。还有其他常见的我可以添加吗?

我的另一个技巧是在 Android.mk 中添加“LOCAL_ARM_MODE := arm”,以加快较新的 arm 芯片的速度(尽管我对这到底是做什么的以及在旧芯片上会发生什么感到困惑)。

【问题讨论】:

标签: android c gcc java-native-interface android-ndk


【解决方案1】:

很好的答案,就像添加你应该使用的那样

APP_ABI := all

这将编译 4 个二进制文件,armv5、armv7、x86 和 mips

您可能需要新版本的 ndk

【讨论】:

    【解决方案2】:

    ARM 处理器有 2 个它们支持的通用指令集:“ARM”和“Thumb”。尽管两者都有不同的风格,但 ARM 指令是 32 位的,而 Thumb 指令是 16 位的。两者的主要区别在于 ARM 指令在一条指令中可以做的事情比 Thumb 做的更多。例如,一条 ARM 指令可以将一个寄存器添加到另一个寄存器,同时对第二个寄存器执行左移。在 Thumb 中,一条指令必须进行移位,然后第二条指令将进行加法。

    ARM 指令没有两倍好,但在某些情况下它们可以更快。在手卷 ARM 组件中尤其如此,可以以新颖的方式对其进行调整,以充分利用“免费换班”。拇指指令有其自身的优势和大小:它们消耗的电池更少。

    无论如何,这就是 LOCAL_ARM_MODE 所做的 - 这意味着您将代码编译为 ARM 指令而不是 Thumb 指令。编译到 Thumb 是 NDK 中的默认设置,因为它倾向于创建更小的二进制文件,并且对于大多数代码来说速度差异并不明显。编译器不能总是利用 ARM 可以提供的额外“魅力”,因此您最终还是需要或多或少相同数量的指令。

    您从编译到 ARM 或 Thumb 的 C/C++ 代码中看到的结果将是相同的(compiler bugs 除外)。

    这本身就兼容当今所有 Android 手机的新旧 ARM 处理器。这是因为默认情况下,NDK 编译为支持 ARMv5TE 指令集的基于 ARM 的 CPU 的“应用程序二进制接口”。这个 ABI 被称为“armeabi”,可以通过 APP_ABI := armeabi 在 Application.mk 中显式设置。

    较新的处理器还支持称为 armeabi-v7a 的 Android 特定 ABI,它扩展了 armeabi 以添加 Thumb-2 instruction set 和称为 VFPv3-D16 的硬件浮点指令集。 armeabi-v7a 兼容的 CPU 还可以选择支持 NEON 指令集,您必须在运行时检查它并提供代码路径,以确定它何时可用,何时不可用。 NDK/samples 目录中有一个执行此操作的示例(hello-neon)。从本质上讲,Thumb-2 更像“ARM”,因为它的指令可以在一条指令中执行更多操作,同时还具有占用更少空间的优势。

    为了编译包含 armeabi 和 armeabi-v7a 库的“胖二进制文件”,您需要将以下内容添加到 Application.mk:

    APP_ABI := armeabi armeabi-v7a
    

    安装 .apk 文件后,Android 包管理器会为设备安装最佳库。因此,在较旧的平台上,它将安装 armeabi 库,在较新的设备上安装 armeabi-v7a 库。

    如果您想在运行时测试 CPU 功能,那么您可以使用 NDK 函数uint64_t android_getCpuFeatures() 来获取处理器支持的功能。这将在 v7a 处理器上返回ANDROID_CPU_ARM_FEATURE_ARMv7 的位标志,如果支持硬件浮点,则返回ANDROID_CPU_ARM_FEATURE_VFPv3,如果支持高级 SIMD 指令,则返回ANDROID_CPU_ARM_FEATURE_NEON。如果没有 VFPv3,ARM 就无法拥有 NEON。

    总而言之:默认情况下,您的程序是最兼容的。由于使用 ARM 指令,使用 LOCAL_ARM_MODE 可能会以牺牲电池寿命为代价使事情变得稍微快一些 - 它与默认设置一样兼容。通过添加 APP_ABI := armeabi armeabi-v7a 行,您将在新设备上提高性能,保持与旧设备的兼容性,但您的 .apk 文件会更大(由于有 2 个库)。为了使用 NEON 指令,您需要编写特殊代码来在运行时检测 CPU 的能力,这仅适用于可以运行 armeabi-v7a 的较新设备。

    【讨论】:

    • 哇,谢谢,很棒的帖子。最后一件事,Android 构建工具是否可以自动编译我的 C 代码的 NEON 和非 NENO 构建,并在运行时为我选择正确的?我假设,当 GCC 知道有 NEON 指令时,它会优化使用它们。这是正确的吗?我可以利用这一点而不必在我的代码中添加 ANDROID_CPU_ARM_FEATURE_NEON 路径和内在函数的使用吗?我对单独的代码路径非常谨慎,因为很难让硬件测试所有代码路径。
    • 不,这是棘手的部分:GCC 不知道——它只是运行时检查。 GCC 只知道您是否使用 v7a。 NEON 指令无论如何都不适用于通用代码。它们是相当具体的向量数学类型指令 - 如果您需要这样做,它们可能会有所帮助,但否则您不会得到任何好处。
    • 感谢richq。我正在做图像处理,所以 NEON 代码会很有帮助。但是,我只是没有硬件来进行所需的测试。无论如何,我什至不确定它的价值:应用程序应该针对最慢的手机(即那些没有 NEON 的手机),而且据我所知,支持 NEON 的手机都很快。
    • 哎呀,对上面的问题很抱歉.. jus 才知道 tegra 也是基于 arm 架构的...... ;)
    • @rbcc:我知道这篇文章写了很长时间,但是如果有人偶然发现这篇文章,就像我现在所做的那样,这里有一个 NEON 条件编译的解决方案:stackoverflow.com/a/12234755/192373跨度>
    猜你喜欢
    • 2017-11-12
    • 2022-11-04
    • 2013-08-27
    • 1970-01-01
    • 2017-02-01
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    • 2013-06-08
    相关资源
    最近更新 更多