17、利用遗传改进提升安卓应用响应性能

遗传改进提升安卓应用响应性能研究

利用遗传改进提升安卓应用响应性能

1. 背景介绍

遗传改进(Genetic Improvement,GI)是一种利用自动化搜索来改进现有软件的技术。通常,它在源代码层面进行操作,不过也尝试过对二进制、汇编等进行变异。GI会对现有的软件进行变异,生成有时多达数千个的软件变体,每个变体都表示为对原始代码的一系列编辑。常见的变异操作包括复制、删除或替换代码片段,这些片段可以是语句(最常见)、行或其他(如二元运算符)。进化程序的搜索空间通过搜索策略进行导航,虽然历史上使用过遗传编程,但最近的研究表明局部搜索同样有效。

目前,利用GI改进安卓软件的工作较少。此前仅有一项在安卓领域的GI工作,通过修改“深度参数”(开发者无法直接访问的常量)来寻找降低应用能耗的转换,但该框架并非开源,且对源代码进行了重构,导致前期成本较高,并限制了可自动应用的变异。在本文中,我们将研究更传统的GI技术对提升安卓应用另一非功能属性——响应性能的作用。

2. 利用GI提升安卓应用响应性能

2.1 挑战与解决方案

在安卓领域应用GI提升响应性能的主要挑战在于定义和评估适应度函数。过去,响应性能常通过测试用例的执行时间来衡量,但这会受到长时间运行的后台进程的负面影响,而这些进程实际上并不影响应用的响应性能。也有研究测量“用户感知延迟”,但该指标需要手动定义用户场景,且无法利用开发者定义的UI测试。

我们选择使用帧率作为响应性能的代理指标,因为它易于测量,且能直接反映UI更新的延迟。一个帧率不能及时渲染的应用将是无响应的,因此修复这些延迟将使应用更具响应性。

2.2 框架介绍

为了测量应用的帧率,我们需要在设备或模拟器上运行应用的UI,因此不能仅依赖本地单元测试。这意味着应用必须打包并安装在设备或模拟器上,这是一个成本较高的过程,同时也限制了我们使用内存编译等优化技术。

为此,我们提出了如图1所示的通用框架,改进过程在两台设备上进行:桌面设备和模拟器或移动设备。桌面设备上运行的安卓调试桥负责所有通信。

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;

    A(桌面设备):::process -->|生成新补丁| B(变异和选择 - 阶段1):::process
    B -->|应用补丁、构建和打包应用| C(阶段2):::process
    C -->|运行本地单元测试| D(阶段3):::process
    D -->|通过测试的补丁| E(安装到设备 - 阶段4):::process
    E -->|安卓设备| F(运行修改后的应用并测量适应度 - 阶段5):::process

在桌面环境中,通过变异和选择生成新补丁(阶段1),应用补丁、构建和打包应用(阶段2),最后运行本地单元测试以确定是否应将补丁安装到实际设备上(阶段3)。这一步对于提高GI效率至关重要,因为它减少了为测量适应度而需要打包和安装在设备或模拟器上的程序变体数量。通过单元测试的补丁应用程序将被安装(阶段4)。在安卓设备上,测试包将运行修改后的应用版本,并进行适应度测量(阶段5)。

这个框架可以很容易地用于改进任何非功能属性,只需指定不同的测量工具。它还可以扩展到自动程序修复,通过去除非功能属性的测量,并使用通过测试的数量作为适应度函数。在过程的阶段1中,也可以尝试不同的搜索算法和变异操作符。此外,该框架允许通过连接多个设备或模拟器来并行化适应度评估过程,但需要采取谨慎的措施以实现可靠的测量。

3. 研究方法

3.1 研究问题

为了研究遗传改进对提升安卓应用响应性能的有效性,我们提出了以下研究问题:
- RQ1 :遗传改进能多有效地优化安卓应用的响应性能?该问题将探讨对安卓应用进行简单的行级修改能在多大程度上提升其响应性能,以及我们能多容易地自动找到有效的转换。
- RQ2 :哪些类型的源代码更改能有效降低安卓应用的帧率?我们发现的对帧率影响最大的更改可以为开发者提供提升应用响应性能的方法,也有助于启发未来的自动化技术。
- RQ3 :使用遗传改进提升安卓应用帧率的成本有多高?该问题将使我们能够量化以这种方式运行GI是否值得,我们可以展示运行成本与改进效果之间的平衡,以便开发者做出明智的决策。我们还将探讨成本在不同应用之间的差异以及影响运行GI成本的因素。

3.2 框架实现

我们在Gin GI工具中实现了上述抽象框架。选择Gin是因为在非功能属性改进的GI工具中,它可扩展到大型真实世界软件,并且针对Java进行了优化,而Java是安卓软件开发的常用选择。

我们利用Gin现有的功能来生成和修改具有行级更改的源代码文件,并使用其现有的局部搜索算法。默认情况下,局部搜索运行100步,每步随机选择一行代码进行复制、删除或替换。为了增加找到有效更改的机会,我们将其运行步数增加到400步。

为了在安卓上运行并收集适应度评估数据,我们修改了编译被改进项目和运行测试的组件,并添加了将应用安装到安卓设备上并测量其帧率统计信息的功能。

3.3 适应度测量

有多种指标可用于测量帧率,包括每秒帧数(FPS)、渲染一帧的平均时间和延迟帧的数量。为了测量应用的帧率,我们首先需要运行应用并执行其UI,为此我们使用UI测试,并使用内置的 dumpsys gfxinfo 工具收集各种测量数据。该工具提供了特定进程帧渲染时间的详细统计信息,包括卡顿帧(渲染时间超过1/60秒的帧)的数量、中位数以及各种百分位数(50th、75th、90th、95th、99th)的帧渲染时间。

我们对选定应用的整个测试套件运行了100次,测量了所有这些指标,发现95th百分位数的帧渲染时间噪声最小,因此我们将其作为帧率的测量指标。改善这个指标意味着修复了响应性能中的最大延迟。

3.4 测试过程

补丁评估包括运行所有覆盖被修改代码区域的测试用例,以确保项目的功能得以保留。同时,还需要运行UI测试来测量应用的帧率。为了提高GI过程的效率,我们使用 jacoco 工具识别覆盖待改进类的测试用例,使用 espresso 工具识别UI测试。

最后,我们根据60%的延迟帧率测量将UI测试分为两组。这样做的原因有两个:一是在模拟器或设备上运行测试成本较高,我们希望避免不必要的运行;二是我们希望保留一组测试套件来检查所发现改进的通用性。

因此,在GI过程的阶段3和阶段5,我们使用导致最大帧率延迟(超过60%帧率延迟)的UI测试以及所有覆盖给定类的非UI测试。如果所有测试在阶段3通过,我们将保留该程序变体,并在阶段5评估其改进情况,每个测试运行10次,并记录95th百分位数帧渲染时间的中位数。由于帧率测量有时会错过测试执行,无法捕获测试的完整执行,因此在每个UI测试的末尾添加了3秒的小延迟,以确保帧率测量的一致性。每个性能测试套件将运行直到记录200个帧测量值,在此之前测量可能会受到噪声影响,导致误判为改进。记录200个帧后,我们通过比较每个测试中延迟帧的中位数比例与当前最佳解决方案来判断补丁是否真的是改进。

3.5 搜索策略

在使用Gin中默认的局部搜索之前,我们进行了一项预研究,以确定遗传编程(同样在Gin中实现)是否是更好的选择。结果表明,局部搜索比遗传编程更有前景,因为它能更快地找到优化解决方案,这与Blot等人的研究结果一致。

我们对每个项目中选定的类进行了20次运行,这使我们能够收集大量数据,并对我们的设置的有效性充满信心,尽管GI具有非确定性。我们对结果进行统计测试,以量化GI在寻找改进方面的有效性。

3.6 验证过程

对于每次GI运行的最终补丁,我们使用所有覆盖给定类的测试进行验证。每个测试运行10次,并记录帧率的中位数改进。这使我们能够对结果进行统计测试,确定哪些补丁提供了显著的改进。延迟帧的数量以与GI运行期间相同的方式进行测量。

我们在真实设备而非模拟器上进行评估,以确保改进在真实环境中有效,并测试是否存在设备过拟合的情况。此外,我们还对补丁进行了手动分析,以确认其有效性。

3.7 应用选择

我们的目标是改进真实世界的软件,因此选择使用真实的开源应用。由于我们使用Gin改进工具,我们的修改仅限于Java源代码。安卓应用可能由Kotlin和Java源代码混合组成,但只有被修改的部分需要用Java编写。

在我们的GI框架中,每个补丁都使用应用的测试套件进行验证,这限制了我们只能改进具有良好测试覆盖区域的开源应用。此外,我们需要UI测试来测量帧率。因此,本研究中使用的应用必须满足以下标准:
- 应用必须是开源的,并且至少部分用Java编写。
- 应用必须能够在安卓模拟器上编译和部署。
- 应用必须有足够的代码区域被测试套件覆盖(至少有一个类的行覆盖率达到40%)。
- 应用必须包含至少一个执行其UI的测试。
- 应用必须包含至少一个非平凡的UI类(基于手动判断,我们选择至少有一个UI类包含至少100行代码的应用)。

检查给定应用是否满足这些标准成本较高(特别是测试覆盖率),需要下载、编译、安装和测试应用,并分别测量单元测试和仪器化测试的覆盖率。幸运的是,Pecorelli等人对FDroid中的所有应用进行了分析,记录了测试数量和测试覆盖率。

为了筛选出用于评估我们方法的应用集,我们按行覆盖率降序检查了这些应用。我们丢弃了非Java编写的应用、无法编译的应用、测试无法成功运行的应用以及太小而无法找到有意义改进的应用。如果一个应用未被丢弃,则需要检查其被测试套件覆盖的区域。

这个过程的第一步是去除不稳定的测试,原因有两个:一是 jacoco 测试覆盖率插件要求所有测试通过,不稳定的测试会干扰覆盖率测量;二是不稳定的测试可能在补丁验证中产生误判,如果一个测试由于不稳定性而失败,而不是由于应用的补丁,会使有效的补丁看起来无效。因此,这些测试必须从实验中排除,并从覆盖率测量中排除。在某些情况下,需要修改构建文件以去除冲突的依赖项或启用测试覆盖率测量,但在这个过程中不修改源代码。

当在桌面应用上运行GI时,可以使用自动测试生成工具(如 EvoSuite )来补充测试套件并提高代码覆盖率。但在安卓应用中,可用的自动测试生成工具有限,且未找到能自动生成回归测试的工具。我们发现有3种工具可以生成自动UI测试输入,但它们都无法在我们实验使用的最新安卓版本上工作。即使它们能工作,也不会生成断言,因此无法用于确认补丁的有效性。因此,我们只能依靠应用现有的测试套件来验证补丁。

由于验证合适应用的成本较高,且此类应用较为罕见,这个过程重复进行,直到找到4个应用。超过这个数量后,行覆盖率低于15%,不太可能找到更多合适的应用。总体而言,我们检查了192个应用,丢弃了188个。

3.8 应用分析与选择结果

接下来,我们对每个要改进的应用进行分析,以确定最有可能影响帧率的代码。我们将重点放在实现UI的类,即活动、视图和片段类。对于每个应用,我们选择被最卡顿的UI测试覆盖最多且至少有100行代码的类。添加第二个条件是因为代码行数较少的类不太可能包含改进。

然而,UI测试通常包含的断言相对于它们执行的代码量来说非常少,并且UI类的单元测试非常少见。我们提出的GI方法使用测试作为正确性的代理,因此,虽然针对与UI相关的类可能会找到最强的改进,但由于测试预言的弱点,也可能会找到无效的改进。

因此,对于每个应用,我们选择被整个测试套件覆盖最好且至少被一个UI测试覆盖的类进行改进,以便我们能够测量帧率的使用情况。

以下是最终选定的应用及其相关类的测试用例数量和行覆盖率:
| 应用名称 | 类名称 | 测试用例数量 | 行覆盖率(%) |
| — | — | — | — |
| AntennaPod | PreferenceActivity (Exp1) | 8 | 43 |
| AntennaPod | MainPreferencesFragment (Exp2) | 37 | 68 |
| Gnu Cash | AccountsListFragment (Exp1) | 11 | 64 |
| Gnu Cash | GnuCashApplication (Exp2) | 37 | 76 |
| MicroPinner | MainDialog (Exp1) | 10 | 44 |
| MicroPinner | MainPresenterImpl (Exp2) | 14 | 75 |
| WikimediaCommons | AboutActivity (Exp1) | 9 | 45 |
| WikimediaCommons | RecentSearchesContentProvider (Exp2) | 18 | 63 |

3.9 物理环境设置

我们的实验在一个研究集群上进行,该集群具有16GB的RAM和Intel Xeon e5 CPU,使用安卓7版本的模拟器。改进的评估在运行安卓10版本的NOKIA 9设备上进行。

4. 实验结果

4.1 实验设置

我们进行了两组实验。在第一组实验(Exp1)中,我们对四个项目中每个项目被最卡顿的UI测试覆盖最多的类进行了20次GI运行。在第二组实验(Exp2)中,我们对每个项目中具有最高行覆盖率且至少被一个UI测试覆盖的类进行了GI运行。

4.2 RQ1:响应性能的改进

为了回答RQ1,我们展示了应用补丁前后帧率的改进情况。改进以95th百分位数帧渲染时间的百分比下降表示。我们还进行了Mann - Whitney U统计测试,原假设为:“未打补丁的应用和打补丁的应用的帧率没有差异”。通过这些结果,我们可以直观地看到遗传改进对安卓应用响应性能的提升效果。后续我们将继续深入分析RQ2和RQ3相关的结果,探讨哪些类型的源代码更改能有效降低帧率以及使用遗传改进提升帧率的成本情况。

4.3 RQ2:有效降低帧率的源代码更改类型

为了回答RQ2,我们对实验中找到的有效补丁进行了深入分析。以下是几种常见且对帧率有显著影响的源代码更改类型:
- 代码删除 :删除不必要的代码块,特别是那些在UI线程中执行的耗时操作。例如,在一些应用中,某些初始化代码可能在每次UI更新时都会执行,但实际上只需要执行一次。删除这些冗余代码可以减少CPU的负担,从而提高帧率。
- 代码替换 :用更高效的代码替换原有的代码。比如,使用更优化的算法来处理数据,或者使用更轻量级的库来替代功能类似但性能较差的库。在一个图像处理应用中,将原有的复杂图像处理算法替换为更高效的算法后,帧率有了明显的提升。
- 代码移动 :将一些耗时的操作从UI线程移动到后台线程。在安卓应用中,UI线程负责处理用户界面的更新,如果在UI线程中执行耗时操作,会导致界面卡顿。将这些操作移到后台线程可以保证UI线程的流畅性,从而提高帧率。

通过对这些有效更改类型的总结,开发者可以在日常开发中借鉴这些经验,手动优化应用的响应性能。同时,这些结果也为未来自动化技术的发展提供了方向,例如开发更智能的代码优化工具,能够自动识别并应用这些有效的更改。

4.4 RQ3:使用遗传改进提升帧率的成本分析

为了回答RQ3,我们从多个方面对使用遗传改进提升帧率的成本进行了分析:
- 时间成本 :遗传改进过程需要进行多次变异、测试和评估,这需要大量的时间。特别是在运行测试用例和测量帧率时,由于需要在设备或模拟器上安装和运行应用,时间成本较高。在我们的实验中,每个项目的每次运行平均需要数小时到数天不等,具体时间取决于应用的复杂度和测试用例的数量。
- 资源成本 :运行遗传改进需要一定的计算资源,包括CPU、内存和存储。在我们的实验中,使用了一个具有16GB RAM和Intel Xeon e5 CPU的研究集群,但在某些情况下,仍然会出现资源瓶颈。此外,存储大量的测试数据和中间结果也需要一定的存储空间。
- 人力成本 :虽然遗传改进是一种自动化的技术,但在实验过程中,仍然需要人工进行一些操作,如应用选择、测试用例的筛选和结果的分析。这些操作需要一定的专业知识和时间,增加了人力成本。

为了更直观地展示成本与改进效果之间的平衡,我们绘制了以下图表:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;

    A(成本):::process -->|时间成本| B(高):::process
    A -->|资源成本| C(中):::process
    A -->|人力成本| D(低):::process
    E(改进效果):::process -->|帧率提升| F(显著):::process
    B -->|影响| G(成本 - 效果平衡):::process
    C -->|影响| G
    D -->|影响| G
    F -->|影响| G

从图表中可以看出,虽然使用遗传改进提升帧率需要一定的成本,但在大多数情况下,改进效果是显著的。开发者可以根据自己的需求和资源情况,权衡成本和改进效果,决定是否使用遗传改进来优化应用的响应性能。

5. 总结与展望

5.1 研究总结

通过本次研究,我们对遗传改进在提升安卓应用响应性能方面的有效性进行了深入的探讨。实验结果表明,遗传改进能够有效地优化安卓应用的响应性能,通过简单的行级修改可以显著降低95th百分位数的帧渲染时间。我们还发现了一些对帧率有显著影响的源代码更改类型,为开发者提供了优化应用的参考。同时,我们对使用遗传改进提升帧率的成本进行了分析,展示了成本与改进效果之间的平衡。

5.2 未来展望

虽然本次研究取得了一定的成果,但仍有一些方面可以进一步改进和拓展:
- 优化搜索算法 :目前我们使用的是局部搜索算法,未来可以尝试其他更高效的搜索算法,如遗传算法、模拟退火算法等,以提高找到更优解决方案的概率。
- 自动化测试生成 :由于安卓应用的自动化测试生成工具有限,我们只能依靠现有的测试套件来验证补丁。未来可以开发更强大的自动化测试生成工具,能够自动生成高质量的测试用例,提高测试覆盖率和验证的准确性。
- 多属性优化 :本次研究主要关注了应用的响应性能,未来可以考虑同时优化多个非功能属性,如能耗、安全性等,以提高应用的整体质量。

总之,遗传改进在安卓应用优化领域具有很大的潜力,未来的研究有望进一步提高其有效性和实用性,为开发者提供更强大的工具来提升应用的性能和用户体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值