5种优化你的安卓5.0 Lollipop代码的方法

Android 5.0 Lollipop* 发布的同时,推出了名为 ART*(简称 Android 运行时)的创新型默认运行时环境。 它包含多项增强功能,可有效提升性能。 本文将介绍 ART 相 比于前代 Android Dalvik* 运行时所具备的部分全新特性,并分享五个技巧,帮助开发 人员进一步提升应用性能。

ART 有何新特性?

基于 Dalvik 运行时分析 Android 应用时,最终用户会面临两个主要难题:应用启动 时间较长以及大量的 jank。 应用断续、不稳定或中止时都会出现 jank,这是因为帧设 置耗费的时间过长,导致应用与屏幕刷新率不一致。 相比于之前的帧,如果一个帧的速度明显加快或减慢,该帧就会被认为出现异常。 用户将这种 jank 视作抖动,用户体验的流畅性会因此比预期的差。 为解决这些问题,ART 增加了几项全新特性:

提前编译: ART 在安装期间使用设备上 dex2oat 工具编译应用,并针对目标设备生成可执行的应用。 通过对比,Dalvik 使用解释器和即时编译,在安装期间将 APK 转换成优化型 dex 字节代码,并在应用运行的时候进一步将该优化型 dex 字节代码编译成面向热路径的原生机器代码。 因此,尽管安装时间长一些,但 有助于加快应用在 ART 中的登录速度。 在 ART 环境中,应用还需使用设备更多的闪存 空间,因为安装期间编译的代码需占用额外空间。

改进内存分配: Dalvik 上需要广泛分配内存的应用可 能会遭遇性能低下问题。 内存分配器中的独立大型对象空间和功能改进有助于解决这一 问题。

改进垃圾回收: ART 的垃圾回收速度更快、规模更大, 可减少碎片数量并提高内存利用率。

提升 JNI 性能: 优化型 JNI 调用和返回代码顺序可减少 JNI 调用时所使用的指令数量。

64 位支持: ART 充分利用 64 位架构,有效提升了基于 64 位硬件运行的应用性能。

由于这些特性,使用 Android SDK 编写的应用以及进行多个 JNI 调用的应用在用户体验方面均得以显著提升。用户还可因此延长电池续航时间,因为应用只需编译一次, 而且在常规使用过程中执行的速度更快,消耗的能源更低。

ART 与 Dalvik 性能对比

ART 首次在 Android KitKat 4.4 上以预览版发布时,其性能遭到了诟病。 但早期的性能对比不太公平,因为对比的对象分别是早期版 ART 与成熟优化版 Dalvik,所以应用在 ART 中的运行速度比 Dalvik 慢。

现在我们可以对比面向客户版本的 ART 和 Dalvik。 由于 ART 是 Android 5.0 内的唯一进行时,并排对比 Dalvik 与 ART 只能基于对比近期从 Android KitKat 4.4 更新 至 Android Lollipop 5.0 的设备。 就本文而言,我们使用采用英特尔® 凌动 TM 处理器的 TrekStor SurfTab xintron i7.0* 平板电脑进行测试,首先是 运行 Dalvik 的 Android 4.4.4,然后更新至运行 ART 的 Android 5.0。

由于我们对比的是不同版本的 Android 系统,Android 5.0 也可能会带来性能改进, 但基于内部性能测试,我们发现,ART 是实现性能改进的最主要原因。

我们运行性能指标评测,其中 Dalvik 能够积极优化重复执行的代码,预计能够带来优势,以及英特尔自己的游戏仿真。

数据显示,在五项性能指标评测中,ART 的表现要优于 Dalvik,而且在几个案例中明显超过了 Dalvik。

Relative Lollipop-ART to KitKat-Dalvik performance
转存失败重新上传取消

如欲了解更多有关这些性能指标评测的信息,请访问下列链接:

Quadrant 2.1.1*
CaffeineMark 3.0*
Smartbench 2012 productivity*
Antutu 4.4 Overall*( 请注意,我们测试的版本不再供下载)
CF-Bench 1.3 Overall*

IcyRocks 1.0 版是由英特尔开发的工作负载,用以模拟真实游戏应用。 它的大部分计算都采用开源 Cocos2d* 函数库和 JBox2D*(一种 Java 物理引擎)。 它测量不同负载水平下每秒渲染的平均动画(帧)数 (FPS),然后通过求取这些 FPS 的平均数计算最 终的指标。 它还测量跳帧的程度(每秒的跳帧数),即不同负载水平下每秒抖动帧数的平均值。 它显示了 ART 较之 Dalvik 所实现的性能改进:

Relative ICY Rocks animations/second

IcyRocks 1.0版也显示ART比Dalvik更连贯地渲染画面而较少出现跳帧,从而获得更平稳的用户体验。

Relative ICY Rocks JANK/second

基于这个性能评估,很显然ART相比Dalvik已经提供了更好的用户体验和更佳性能。

代码从 Dalvik 到 ART 的转换

代码从 Dalvik 到 ART 的转换非常透明,大多数运行于 Dalvik 的应用可直接运行于 ART,无需进行修改。 因此,用户升级至新的运行时后,许多应用都可实现性能改进。 最好测试带有 ART 的应用,尤其是使用 Java 原生接口的应用,因为 ART 的 JNI 错误处理比 Dalvik 更严格,如本文所述

优化代码的 5 个技巧

得益于 ART 的上述功能改进,大多数应用将实现性能提升。 此外,您还可采用其他实践进一步针对 ART 优化您的应用。 我提供了部分简化的代码,以证明下列各种技巧的运行方式。

由于应用各不相同,其性能主要依赖于周围的代码和环境,因此无法为您提供明显的性能提升指示。 不过我们将解释这些技巧能够提升性能的原因,并建议您在自己的代码 环境中测试这些技巧,以验证它们对性能的影响。

这些技巧的使用非常广泛,但在 ART 案例中,可通过 dex 文件生成二进制可执行代码的 dex2oat 编译器将实施下列优化。

技巧 #1 – 尽可能使用本地变量,不要使用公共类域。

限制变量范围后,不仅可以增加代码的可读性,降低错误率,还可使代码更加便于优化。

在下列未优化代码中, v 的值在应用运行的时候计算。 因为 v 可绕过方法进行访问,它的值可通过任意代码更改,因此在编译期间处于未知状态。 编译器不知道 some_global_call() 操作是否更改了 v,因为任何代码都可绕过该方法更改 v。

在优化代码中,v 是本地变量,它的值可在编译期间计算。 因此,编译器能够直接将结果放在代码中,从而避免在运行时进行计算。

未优化代码

class A {
  public int v = 0;

  public int m(){
    v = 42;
    some_global_call(); 
    return v*3; 
  }
}
  • 1

优化代码

class A {
  public int m(){
    int v = 42;
    some_global_call();
    return v*3; 
  } 
}
  • 1

技巧 #2 – 使用关键词 final 提示该值为常量。

关键词 final 可用于防止代码被意外修改应为常量的变量,还可通过提示编译器该值为常量来提高性能。

在下列未优化代码中,v*v*v 的值必须在运行时计算,因为 v 的 值可以更改。 在下列优化代码中,为 v 赋值时,使用关键词 final 将告知编译器,该值不会被更改,因此可在编译期间执行计算,并将计算结果添加至代码,从而无需在运行时进行计算。

未优化代码

class A {
  int v = 42;

  public int m(){
    return v*v*v;
  } 
}
  • 1

优化代码

class A {
  final int v = 42;

  public int m(){
    return v*v*v;  
  } 
}
  • 1

技巧 #3 – 使用关键词 final 定义类和方法

因为 Java 中的所有方法具备多形态特征,声明方法或类的最终性可告知编译器,该方法在任意子类中都不会被重新定义。

在下列未优化代码中,m() 必须在执行调用之前明确。

在优化代码中,由于方法 m() 已声明为最终方法,编译器知道应该调用哪个版本的 m()。 这样可避免方法查找和行内调用,从而该方法的内容可替换 m() 调用。 性能会因此显著提升。

未优化代码

class A {
  public int m(){
    return 42;  
  } 
  public int f(){
    int sum = 0; 
    for (int i = 0; i < 1000; i++)
      sum += m(); // m must be resolved before making a call 
    return sum;
  }
}
  • 1

优化代码

class A {
  public final int m(){
    return 42;  
  } 
  public int f(){
    int sum = 0; 
    for (int i = 0; i < 1000; i++)
      sum += m();  
    return sum;
  } 
}
  • 1

技巧 #4 – 避免 JNI 调用小方法。

在很多情况下都可使用 JNI 调用,例如能够重复利用 C/C++ 代码库或函数库时,需要跨平台实施时,或需要性能提升时。 但最好能够最低限度地减少 JNI 调用的数量,因为调用的开销非常大。 如果 JNI 调用用于优化性能,其开销可能导致无法达到预期效果 。 尤其是频繁调用简短 JNI 方法可能会产生负面影响,而且将 JNI 调用放在循环中会增加开销

代码示例

class A {
  public final int factorial(int x){
    int f = 1;
    for (int i =2; i <= x; i++)
      f *= i;
    return f;  
  } 
  public int compute (){
    int sum = 0; 
    for (int i = 0; i < 1000; i++)
      sum += factorial(i % 5); 
// if we used the JNI version of factorial() here
// it would be noticeably slower, because it is in a loop
// and the loop amplifies the overhead of the JNI call
    return sum;
  }
}
  • 1

技巧 #5 – 使用标准函数库,不要用自己的代码实施相同的功能

标准 Java 函数库经过了高度优化,通常使用内部 Java 机制获取最佳性能。 相比于使用您自己的代码实施相同功能,这种运行方式的速度要快得多。 试图避免因调用标准函数库而产生的开销实际上会导致性能下降。 在下列未优化代码中,有种自定义代码可 用来避免调用 Math.abs()。 不过,这种使用 Math.abs() 的代码运行速度会比较快,因为在编译期间,Math.abs() 由 ART 中的优化型内部实施所替代。

未优化代码

class A {
  public static final int abs(int a){
    int b;
    if (a < 0) 
      b = a;
    else
      b = -a;
    return b;
  } 
}
  • 1

优化代码

class A {
  public static final int abs (int a){
    return Math.abs(a); 
  } 
}
  • 1

英特尔对 ART 的优化

英特尔与 OEM 合作提供优化版 Dalvik,基于英特尔处理器运行时可提升性能。 英特尔同样投资于改进 ART,使其基于全新运行时进一步增强性能。功能优化将通过 Android 开源项目 (AOSP) 提供,并/或直接通过设备厂商提供。 同先前一样,功能优化对开发人员和用户是透明的,因此无需更新应用便可轻松获得优势。

了解更多信息

如需了解更多有关优化面向英特尔处理器的 Android 应用,以及英特尔® 编译器的信 息,请通过网址 https://software.intel.com 访问英特尔开发人员专区。

关于作者

Anil Kumar 在英特尔公司工作已超过 15 年,在软件与服务事业部担任过多项职务。目前,他担任高级资深软件性能架构师,并在以下方面发挥着积极的作用:通过服务于各大标准组织帮助完善 Java 生态系统、积极参与性能指标评测(SPECjbb*、SPECjvm2008 ,SPECjEnterprise2010 等)、通过提升用户体验和资源利用率改进客户应用,以及提升面向软硬件配置的默认性能。

Daniil Sokolov 是英特尔软件与服务事业部的资深软件工程师。 过去 7 年,Daniil 一直专注于 Java 性能的各个领域。 目前,他主要致力于提升基于英特尔 Android 设备的用户体验和 Java 应用性能。

Xavier Hallade 是英特尔软件与服务事业部常驻法国巴黎的开发人员宣传官,主要致力于各种 Android 框架、函数库和应用,帮助开发人员增强对新硬件和新技术的支持。
他同时还是 Android 领域的谷歌开发专家,专注于 Android NDK 和 Android TV。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春哥111

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值