小铲子 发表于 2016-1-29 17:06:38

在 x86 上优化 Android 应用的方法和技巧


英特尔致力于帮助开发人员提供能够在英特尔架构上正常运行(甚至出色运行)的 Android 应用。 虽然英特尔主要致力于社区层面:优化 Dalvik Java、V8 引擎和 Bionic C;丰富代码库;为 IA 提供采用 32 位和 64 位内核的版本,他们同样提供了多种类型的新工具为 Android 开发人员提供帮助。 这些工具主要用于提高性能,使其超过面向 x86 的默认 ARM 转换层:libhoudini 所提供的性能。但是首先需要选择合适的工具。 创建 Android 应用有 3 种常见的方法。
[*]Java 使用 Android SDK API 进行编写,以便在 Dalvik VM 中运行。​ 注: 近期,我们还将针对 Android L 发布一篇包含 ART 的文章。
使用最新的 SDK 能够处理大部分的差异,但是您可能还希望了解为高分辨率屏幕分配的内存。 最明显的一点是,如果使用英特尔®HAXM 加速 Android 模拟软件(需要使用英特尔® 虚拟技术和 XD,将两个功能都设置为“开”),测试速度将更快。
[*]以 Web 为重心的 HTML5 和 JavaScript。   关于开源 Android 信息,请查看 Android-IA 网站。
[*]创建或移植的 NDK (在 C++ 中编写)。 如果您准备部署处理器密集型功能或已经使用了 C++ 代码,则推荐该方法。 通常(并非一直),原生 C++ 代码在与硬件使用“相同”语言时运行速度更快,因为代码在运行前已经编写到二进制中,无需中断机器语言。
本文主要介绍优化基于 NDK 的应用。 这些应用可能只包括 C/C++ 代码或包括第三方库和/或汇编码。

注: 如果您没有 Android 开发环境 (IDE),新工具套件英特尔® INDE (英特尔®集成式本机开发人员体验)将加载一款指定的 Android IDE,并下载和安装多款英特尔工具,帮助您创建和编写 Android 应用,为其执行故障排除并发布。点击这些链接,了解如何注册和安装英特尔® INDE并使用 Eclipse* IDE 进行安装,并观看安装 NDK & SDK*、Eclipse*,或在模拟器(包括如何加速)或基于英特尔® 架构的设备运行的视频。从高层面而言,NDK 开发包括以下步骤,且仅需进行较少的改动即可配合 x86 架构使用。
[*]创建 Android 项目和 jni 文件夹。 编辑 Application.mk,以显示 APP_ABI = all (如果文件尺寸允许将 ARM* 和 x86 放在同一个软件包内。)或 x86。注: APP_ABI 设置还会影响浮点操作 — 请见下文。
[*]代码。 所有原生 (C++) 代码都可重复使用。 重新编写内嵌汇编代码或 ARM 特定代码。使用 javah 创建 JNI/原生代码头文件。确保使用 JNIEXPORT 和 JNICALL 宏在 Windows 标准 C++ 约定和 Java/JNI 进行中断。
[*]编写/构建库(调用生成 .so lib,并将其存入适当的项目目录下)。 使用 "ndk-build APP_ABI = X86 ",并对 build flag 做出几处改动,见下文。 同样需要对第三方库进行重新编写。
[*]从 Java 进行调用。在 Java 中**原生 ( C++) 函数调用,使用 System.loadlibrary() 加载共享库。
[*]可以运行将清单设置为可调试的 ndk-build 来使用 Debug. ndk-gdb 调试。 确保将 adb 目录添加至 PATH,且仅运行一个目标。
除了基本的“移植”,还可以使用一些优化。优化方法:
[*]针对硬件辅助模拟,使用 英特尔®HAXM 加速基于软件的 Android 模拟器。 英特尔®HAXM 要求将英特尔®虚拟化技术(英特尔® VT)和 XD 设置为“开启”。
[*]设置 APP_ABI = x86 (创建一个包含所有二进制的 apk)或= armeabi armeabi-v7a x86,具体取决于您的文件尺寸限制.. (请注意,x86 包括硬件浮点,与 armeabi-v7a-x86 相同(从某种程度上))
[*]在编译过程中,使用 gcc "-malign-double"。 (它用于内存校准,另见 #9)
[*]在编译过程中,添加适当的 CPU 线程 flag
对于英特尔®凌动™ 处理器的超线程功能,请尝试使用            -mtune=atom -mssse3 -mfpmath=sse
对于非超线程, (BYT, SLM, Merrifield) 使用                              -mtune=slm -msse4.2 -mfpmath=sse
使用 -march= 限制指定 CPU(mtune 仅可在某些型号上运行,但是针对列出的类型均进行了优化)。
-mavx 还无法在凌动上使用。
[*]使用 little Endian (默认在 NDK 中随附)。ARM* 支持 big 和 little 两种 Endian,英特尔® 凌动™ 仅支持 little,因此请查看 gcc flag。
[*]使用 v 4.8 的 gcc。 观察 2 个工具链路径 (android-ndk\toolchains\arm-linux-androideabi-4.8 和 x86 android-ndk\toolchains\x86-4.8
[*]确保使用正确的 JNIEXPORT 方法签名,在原生代码中设置输入方法(确保头文件的函数签名匹配,以确保在 Windows* 上编译源代码)。
JNIEXPORT void JNICALL Java_ClassName_MethodName
[*]编译后,检查系统日志,确保目标原生 lib 在运行时成功加载。 (它在日志中显示为 "added shared lib //<path>"
[*]明确强制执行内存校准,以防止出现加载错误和网络数据包故障。ARM 占用 24 位,但是需要为 64 位变量实施 8 位校准,而 x86 占用 16 位,因此请确保数据结构为 16 位校准。 当在从该架构向 XMM 寄存器加载时,使用校准移动 (MOVAPS, MOVNTA) 。 参阅降低不一致的内存访问的影响。
[*]直接向主内存 (streaming stores instructions MOVNTPS, MOVNTQ) 中编写数据,因为英特尔® 凌动™ 处理器没有三级高速缓存, 这可避免高速缓存回收上的脏回写,从而节约带宽消耗。
[*]避免由于二级高速缓存受限而导致的停顿。 除了特定实例外(加载和存储数据的地址相同,操作数尺寸相同,通过通用寄存器执行),当编写至高速缓存时,英特尔® 凌动™ 处理器将停止数个循环。 此外,存储 SSE 操作数(从xmm 寄存器)从来不会转发至后续加载中,
无论转发或不转发,请尝试使用寄存器内的数据在 xmm 寄存器内计算总数。   对于 mp3 解码器中的示例,有一个窗口循环在寄存器中累积/计算总数,然后跨寄存器计算总数。
这会导致存储转发在存储到 pSum 阵列的 16 位存储和接下来从 pSum 上执行的 4 位加载之间卡住。 为了避免这一点,请使用 HADDPS 指令或一套 add 和 shuffle 计算 xmm 寄存器中的横向之和。 (请注意,HADDPS 序列在英特尔® 凌动™ 处理器上的速度更快,在英特尔酷睿处理器的许多变量上速度更慢。)。 利用 SSE 最小化和最大化指令,在超过 16 位范围的示例上剪辑。
[*]在使用前将整个 XMM 寄存器(MOVLPS、MOVHPS、PINSRW)归零,因为一些指令只能加载部分寄存器,这导致代码上的问题出现在其他部分。
[*]阅读优化SIMD 指令的文章(英特尔 SSE vs. ARM* NEON* )。考虑使用此处的 NEONvsSSE_5.h 库,(替代 arm_neon.h)。本文还提到,当使用常数(请勿在循环中添加初始化,如果可以,请使用逻辑/比较运算进行替换)并避免串行实施(使用矢量)时,将影响性能。


页: [1]
查看完整版本: 在 x86 上优化 Android 应用的方法和技巧