【问题标题】:Android NDK: Providing library variants for the same abiAndroid NDK:为同一个 abi 提供库变体
【发布时间】:2015-03-31 10:01:41
【问题描述】:

我正在寻找最好的方法来开发和打包具有不同编译设置但用于相同 ABI 的库的不同变体,然后在运行时选择最适合的库。更具体地说,我想要一个 NEON 和非 NEON armeabi-v7a 版本。

本机库具有第三方链接到的公共 C 接口。他们似乎需要链接到其中一个变体以防止链接错误,但如果它更适合设备,我想在运行时加载替代变体,并让运行时加载器进行正确的重定位。

从我目前看到的情况来看,我似乎需要为两个变体提供相同的文件名,因此需要将它们放在不同的文件夹中。 abi 文件夹下的子文件夹似乎没有被包安装过程复制,因此这种方法不起作用。到目前为止,我看到的最好的建议是手动将一个变体从 res 文件夹复制到已知的设备路径,并使用完整路径调用 System.loadLibrary()。参考:https://groups.google.com/forum/#!topic/android-ndk/zu_dmcmUlMo

  1. 这仍然是最好的/推荐的方法吗?
  2. 这将如何与在非 arm 设备上完成的二进制转换交互? (虽然我可以提供 x86 版本,但一些第三方可能会将其排除在他们的 apk 之外)。

我假设使用二进制翻译的设备上的 cpufeatures 不会将 cpu 系列报告为 ARM,所以我建议的解决方案是以正常方式构建标准 armeabi-v7a 库(我猜这会得到二进制翻译) ,并在 res/raw 中发布支持 NEON 的库。然后在运行时,如果 cpufeatures 报告支持 NEON 的 ARM CPU,则复制该库并使用完整路径调用 loadLibrary。有人能看出这种方法有什么问题吗?

【问题讨论】:

    标签: android-ndk shared-libraries


    【解决方案1】:

    如果您明确希望有两个不同的库版本,那么是的,这可能是最好的折衷方案。

    首先 - 请注意,许多可以使用 NEON 的库可以使用那些启用运行时的部分构建,这样您就可以拥有正常的 ARMv7 构建,它并不严格要求 NEON,但如果检测到,可以在运行时启用这些代码路径 -例如libav/FFmpeg 可以做到这一点,许多其他类似的库也是如此。这允许您拥有一个在适用的情况下充分利用 NEON 的单一 ARMv7 二进制文件,同时仍可在少数没有 NEON 的 ARMv7 设备上工作。

    如果您尝试使用编译器自动向量化,或者如果这是一个库,其中 NEON 例程不容易局限于在运行时启用的受限部分(或者希望通过使用 NEON 构建整个库来获得额外的性能启用),你的方法听起来很理智。

    请记住,您希望至少拥有一个“正常”打包的本机库(您似乎拥有,但在例如https://stackoverflow.com/a/29329413/3115956 中一直是个问题)。安装时,安装程​​序会选择捆绑架构的最佳匹配,并仅从该架构中提取库,并以该模式运行进程。在具有多个 ABI(32 位和 64 位)的设备上,这是必不可少的,因为如果进程以不同的模式启动,那么一旦您尝试以不同的形式加载库,就太晚了。

    在模拟 ARM 二进制文件的 x86 设备上,如果进程在 ARM 模式下运行,至少 cpufeatures 库将返回 ARM。如果您使用系统属性来查找主要和次要 ABI,您将不知道当前进程正在使用它们中的哪一个。

    编辑:具有二进制翻译的 x86 设备实际上似乎能够加载 armeabi 库,即使同一个进程也已经加载了一些捆绑的 x86 库。因此,显然这种翻译是基于每个库完成的,不像 32 位和 64 位那样,在启动时为进程选择了某种模式,这不包括加载其他变体的任何库。

    【讨论】:

    • 谢谢。您确定 cpufeatures 在 x86 设备上返回 ARM 吗?我认为他们在安装时将二进制转换为 x86 而不是实际的 ARM 仿真?我真的需要一个 x86 设备来测试。
    • 虽然我一般会尝试转向运行时检查,但我假设每个调用都需要检查通用(非 NEON 编译)代码中的 neon,然后从编译的单独文件调用一个函数启用 NEON。这非常混乱,并且阻止了 NEON 代码被内联。
    • 嗯,cpufeatures 库只返回编译它的架构(看看<NDK>/sources/android/cpufeatures/cpu-features.c 中的android_cpuInitFamily),所以如果它最初是一个ARM 二进制文件,它总是会报告ARM。
    • 我很确定二进制翻译是在运行时发生的,而不是在安装时发生的——但它似乎比我想象的更强大。我尝试做一个包含 arm 和 x86 库的 APK,但在 res/raw 中有一个仅 arm 的库。由于有一个 x86 版本,它将使用那个版本,但是来自 res/raw 的 arm 库也可以很好地加载到同一进程中,因此显然翻译是每个库的。这与例如相反。 armeabi vs arm64-v8a,如果进程以 64 位模式启动,则无法加载 32 位库。我将编辑帖子并澄清这一点。
    • 是的,如果你有运行时检查,你就不能有内联的 NEON 代码。许多库(例如编解码器和类似代码)大多在 DSP 例程中具有 NEON 代码(例如“对这个数组进行卷积”),并且具有确定要使用的此类函数的正确版本的设置代码,然后您只能通过函数使用它们指针。您可能会在某处错过一些潜在的内联使用,但您仍然可以从 SIMD 中获得主要的批量优势。我最近看到的大多数编解码器库至少都是这样做的。
    猜你喜欢
    • 2019-08-07
    • 2016-04-17
    • 1970-01-01
    • 2019-02-13
    • 2011-10-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多