eoe 移动开发者论坛

 找回密码
 加入eoe

QQ登录

只需一步,快速开始

查看: 242565|回复: 0
收起左侧

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

  [复制链接]

该用户从未签到

51

主题

70

帖子

782

e币
发表于 2014-12-12 16:09:46 | 显示全部楼层 |阅读模式

没有eoe的账号,级别还太低,出门如何吹牛逼?

您需要 登录 才可以下载或查看,没有帐号?加入eoe

x
英特尔致力于帮助开发人员提供能够在英特尔架构上正常运行(甚至出色运行)的 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 为重心的 HTML5JavaScript。   关于开源 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) 。 参阅[url=]降低[/url]不一致的内存访问的影响
  • 直接向主内存 (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)。  本文还提到,当使用常数(请勿在循环中添加初始化,如果可以,请使用逻辑/比较运算进行替换)并避免串行实施(使用矢量)时,将影响性能。
  • 使用表格查找运算、倒数近似值(RCPPS 指令)或Newton-Raphson 序列替换掉除法和平方根运算,因为后者需要执行许多次循环。
  • 观察浮点调用。 使用浮点而非双精度(因为双精度经常使用 SW lib 例程)。 在英特尔® 凌动™  处理器上,浮点速度更快,且能够节省内存带宽。 而且,APP_ABI 可设置是否使用 SW (armeabi) 或 HW (X86, armeabi-v7a x86) 浮点。 您不希望一直使用 x86 算法执行全部 HW FPU 计算。 (例如,在整数代码除以 2 的幂数是快速、正确的移位操作,但是对于 Android 优化,您需要乘以倒数 (y=x*.5 而非 y=x/2)
  • 减少小型函数的支出。 在参数传递、设置新堆栈帧/还原旧堆栈帧/保留调用程序的堆栈帧、在堆栈中存放地址等位置使用内联函数;返回值调用,然后返回函数。 另请参阅 英特尔® 64 和 IA-32 架构优化参考手册

*滑动验证:
您需要登录后才可以回帖 登录 | 加入eoe

本版积分规则

推荐阅读
赞助商们

QQ|联系我们|小黑屋|手机版|eoe 移动开发者论坛 ( 京ICP备11018032 京公网安11010802020210  

GMT+8, 2017-11-23 15:13 , Processed in 0.550298 second(s), 46 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表
关闭

扫一扫 关注eoe官方微信