【腾讯Bugly干货分享】TRIM:提高磁盘性能,缓解Android卡顿

Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,经过平常工做经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处。java

在业内,Android 手机一直有着“越用越慢”的口碑。根据第三方的调研数据显示,有77%的 Android 手机用户认可本身曾遭遇过手机变慢的影响。他们不明白为何购买之初“如丝般顺滑”的 Android 手机,在使用不到一年以后都会“卡顿”得让人抓狂!根据咱们初步的测试数据,手机长期所使用产生的磁盘碎片可使得磁盘的写入效率降低为原来的50%。是否是有一种“吓死本宝宝了”的感受。缓存

那么怎么办呢?笔者曾经对这一问题进行分析,且让我一一贯你道来。markdown

故事原由

故事的原由是,针对“Android 系统越用越卡的问题”,腾讯某产品团队但愿在自身产品中进行优化,从而提高产品口碑。网络

通过简单的分析讨论,你们认为形成这种现象多是因为两个方面缘由:内存、磁盘:架构

  • 先说内存。为了保证应用能够快速被再次调起,Android 在内存管理上采用以下策略:进程保持在内存中,在占用内存未超过阈值以前不会系统进行主动清理。但随着应用的增多,试图保持在内存中的进程将会增多,所以影响系统的流畅度。能够说,内存与系统卡顿的关系早已经是业界的共识,其解决方案也比较明了,即赋予系统主动清理内存的能力,例如待机后杀掉没必要要的进程。app

  • 再聊磁盘。长期使用 Android 手机必将产生大量的磁盘碎片,而磁盘碎片将会下降磁盘的读写性能,从而影响系统流畅度。可是磁盘碎片是否能对磁盘的读写性能形成了很大影响,以致于影响系统流畅度还没有可知,且暂时也没有发现能够进行尝试的潜在优化点。工具

因而,产品团队找到了咱们专项测试组,但愿分析 Android 越用越卡与磁盘是否有关系,并初步探索系统在磁盘管理模式方面是否存在潜在优化点。这就有了下文。性能

逐步分析

Step 0:磁盘与系统流畅度的关系

其实,平常的生活经验(例如 SSD 可让老笔记本焕发新生)已经咱们可以感受获得磁盘对系统流畅度的影响很大。可是,这里仍是有再必要简单说一下,磁盘是如何影响系统流畅度的。学习

开发过 Android 项目的同窗都知道 Android 在使用网络的最佳实践是使用3级缓存的设计来提高系统的流畅度并节省流量:CPU 首先尝试从内存中加载图片,若此时图片存在在内存中则加载成功,不然内存会从磁盘中加载图片,若此时图片存在在磁盘中则加载成功,不然磁盘会最终向网络中下载图片。测试

其实上述的执行逻辑,也就解释了磁盘是如何影响系统流畅度的:对于系统流畅度(其实也是各个应用的流畅度)影响最直接的就是 CPU 的执行效率,可是若是这个过程当中内存、磁盘以及网络的读写速度若是跟不上 CPU 的执行效率的话,就会形成 CPU 在处理任务的时候须要花费时间等待数据,从而影响了流畅度。

因此第一个问题就弄清楚了:磁盘的读写速度的下降会使得系统流畅度变差!那么,咱们要分析的问题就转化成:磁盘在长期使用的过程当中,其读写速度会不会下降。

Step 1:弄清 Android 磁盘的读写机制

为了分析清楚磁盘“磁盘在长期使用的过程当中,其读写速度会不会下降”这个问题,咱们有必要先弄明白 Android 磁盘所采用的读写机制。

经过资料查阅,咱们了解到目前,Android 手机大多采用 NAND Flash 架构的闪存卡来存储内容。NAND Flash 的内部存储单位从小到大依次为:Page、Block、Plane、Die,而一个 Device 上能够封装若干个 Die。下图就是一个 NAND Flash 组成结构的示意图。

NAND Flash组成结构的示意图

为了方便理解,针对一个 Die,咱们再抽象一下,Page、Block、Plane、Die 的关系以下图所示。

NAND Flash中Page、Block、Plane、Die的关系

虽然 NAND Flash 的优势多多,可是为了延长驱动器的寿命,它的读写操做均是以 Page 为单位进行的,但擦除操做倒是按 Block 为单位进行的。

因为有大量的读写操做,因而咱们的 NAND Flash 制定了以下的读写规则:

  1. 删除数据时,芯片将标记这些 Page 为闲置状态,但并不会立马执行擦除操做。
  2. 写入数据时,若是目前磁盘剩余空间充足,则由芯片指定 Block 后直接按 Page 为单位进行写入便可。
  3. 写入数据时,若是目前磁盘剩余空间不足,为了得到足够的空间,磁盘先将某块 Block 的内容读至缓存,而后再在该 Block 上进行擦除操做,最后将新内容与原先内容一块儿写入至该 Block。

那么问题来了!假如如今我要向磁盘中写入一张图片的数据,这个图片的数据大小恰好为一个 Page。最坏的状况就是,内存中刚好只有一个 Block 刚好有一个 Page 的无效数据能够擦除。为了存下这张图片,因而主控就把这个 Block 的全部数据读至缓存,擦除Block上的内容,再向缓存中加上这个4KB 新数据后最后写回 Block 中。

个人天啊,其实想存储的就是1个 Page 的图片内容,可是实际上确形成了整个 Block 的内容都被从新写入,同时本来简单一步搞定的事情被还被分红了先后四步执行(闪存读取、缓存改、闪存擦除、闪存写入)形成延迟大大增长,速度变慢。这就是传说中的“写入放大”(Write Amplification)问题。而“写入放大”也说明了磁盘在长期使用的过程当中,其读写速度(尤为是写入速度)会存在下降的现象。

Step 2:解决“写入放大”问题的技术——TRIM

不过,既然“写入放大”(Write Amplification)都这么出名了,确定不会没有现成的解决方案的!这个很简单,Google 一下,咱们就知道解决方案就是 TRIM 技术。

TRIM 是一条 ATA 指令,由操做系统发送给闪存主控制器,告诉它哪些数据占的地址是“无效”的。在 TRIM 的帮助下,闪存主控制器就能够提早知道哪些 Page 是“无效”的,即可以在适当的时机作出优化,从而改善性能。这里要强调下,TRIM 只是条指令,让操做系统告诉闪存主控制器这个 Page 已经“无效”就算完了,并无任何其它多余的操做。在测试的过程当中,咱们发现 TRIM 的触发须要操做系统、驱动程序以及闪存主控三者都支持才能真正意义上实现。例如:

  • 操做系统不支持的状况:Android 4.3如下均不支持
  • 闪存主控不支持的状况:Samsung Galaxy Nexus(I9250)所选用的闪存不支持

基于 TRIM 技术,目前常见有两种方案能够解决“写入放大”的问题:

  1. discard 选项。该方案将在挂载 ext4 分区时加上 discard 选项,此后操做系统在执行每个磁盘操做时同时都会执行 TRIM 指令。该方案的优势是整体耗时短,但影响会到删除文件时的性能。
  2. fstrim 命令。该方案将选择合适的时机对整个分区执行 TRIM 操做。相对于方案一,该方案整体耗时较长,但不会影响正常操做时的磁盘性能。

不得不说,若是从用户的角度出发,仍是 FSTRIM 的方法更靠谱一些,但如何寻找合适的 TRIM 时机就是一个比较讲究的问题了。

Step 3:TRIM 在 Android 中的实现

根据前面的分析,咱们不难理解在 Android 中的 TRIM 选择经过 fstrim 命令的方式进行实现。那么,Google 又是如何设计触发TRIM的时机呢?

经过走读 Android 源码(AOSP 4.4.4),能够了解到 Android 经过系统服务 IdleMaintenanceService 来进行系统状态监控并决定什么时候触发 TRIM。根据 IdleMaintenanceService.java 源码,咱们绘制了 fstrim 的触发示意图以下:

注释:

  • 有/无操做:距屏幕熄灭||屏保启动已超过71分钟
  • 是/否电量充足:维护期20%,非维护期(充电状态30%,非充电状态80%)
  • 是/否维护超时:启动维护已超过71分钟
  • 是/否已到维护期:据上次启动维护超过1天

Step 4:分析闪存碎片及 TRIM 对磁盘 I/O 性能的影响

了解了这么多技术背景,那咱们经过测试数据分析闪存碎片和 TRIM 对磁盘 I/O 性能的影响。根据测试目的,具体的测试设置以下:

  • 测试目的

评估闪存碎片和TRIM对磁盘 I/O 性能的影响

  • 测试方案

测试对象:LG Nexus 5 with cm-11-20140805-SNAPSHOT-M9-hammerhead

测试步骤

  1. 从新刷机,使用 Bonnie++ 测试 SD 卡目录的 I/O 性能;
  2. 模拟长期使用 SD 卡的过程(期间须要避免TRIM触发),使用 Bonnie++ 测试 SD 卡目录的 I/O 性能;
  3. 主动触发 TRIM,使用 Bonnie++ 测试 SD 卡目录的 I/O 性能。

备注:

  1. 模拟长期使用 SD 卡的过程的方法:开发专用的测试应用,该应用将向 SD 卡目录不停写入大小随机的文件,当 SD 卡剩余空间不足时将删除所写入的文件,而后继续上述操做直到应用退出。
  2. 避免 TRIM 触发的方法:根据 Android 的触发过程分析,只需设置屏幕常亮并便可避免 TRIM 的触发。
  • 测试数据

数据解读:

  1. 经过反复擦写 SD 卡,能够发现 SD 卡的 I/O 效率指标均存在必定幅度的下滑,其中反映磁盘空间分配性能及文件数据写回性能的指标下滑明显;
  2. Sequential Output-Block 能够反映分配磁盘文件空间的效率,经反复擦写 SD 卡后,该效率下降至原始值的15-20%,应该是大量的磁盘闲置数据块形成的影响;
  3. Sequential Output-Rewrite 能够反映文件系统缓存和数据传输的速度,经反复擦写 SD 卡制造闲置数据块后,该效率下降至原始值的50%。
  4. 主动调用 TRIM 后,能够发现 SD 卡的 I/O 效率指标均恢复至接近原始值水平(但仍未彻底达到初始状态的水平)。
  • 测试结论
  1. 在 TRIM 无效的状况下,长期使用 SD 卡,磁盘写入速度会受到明显影响;
  2. TRIM 对因闲置数据块形成的 I/O 性能降低有必定的恢复做用;
  3. 大量的读写操做对 SD 卡形成了必定量的不可恢复的损耗。

Step 5:FSTRIM 系统自动触发测试

完成了上面的工做,不禁得让咱们大吃一鲸:原来 TRIM 对 SD 卡的读写速度的维护如此重要!前面也说到,Android 选择 FSTRIM 方案的来实现 TRIM,那么 Android 所设计的 FSTRIM 触发时机有没有什么问题呢?

根据 Android 系统的设定,FSTRIM 预期是每隔24小时触发一次。因此,接下来咱们须要评估一下,FSTRIM 可否依据上述设定成功被系统触发。

  • 测试目的

分析 FSTRIM 可否被按时被系统触发

  • 测试方案

测试对象:2台 Samsung Galaxy Nexus 及2台 LG Nexus 5

测试步骤

  1. 刷机后,安装经常使用应用并启动(均无SIM卡,其中1台设备开启 Wifi,另1台设备关闭 Wifi);
  2. 进行 Log 记录;
  3. 强制执行一次 FSTRIM;
  4. 灭屏等待30小时左右,提取 Log 记录进行分析。
  • 测试数据
开启WiFi 关闭WiFi
Samsung Galaxy Nexus 启动FSTRIM 1次 启动FSTRIM 1次
LG Nexus 5 未启动FSTRIM 启动FSTRIM 1次

数据解读:

  1. FSTRIM 大多数状况会被自动触发,但也存在没法触发的状况;
  2. 根据 FSTRIM 的触发逻辑,是否开启 WIFI 对 FSTRIM 的影响主要是有无推送消息(影响灭屏条件)以及不一样的耗电。
  • 测试结论

测试数据显示 FSTRIM 大多数状况会被自动触发,但也存在没法触发的状况。可能的缘由是:FSTRIM 对电量的要求略高,因此一旦发生意外状况(如应用的 PUSH 消息)终止了计划 FSTRIM 的执行以后,很长时间以内都没法再知足 FSTRIM 的启动条件。

因此,如需提升其触发频率,咱们能够考虑下降触发条件中对电量的要求。

总结&思考:

根据前面的分析,咱们能够从 Android 源码及测试数据对前面两个问题作出回答:

  1. 磁盘碎片(更准确的说法是 SD 卡中的闲置数据块)会严重影响磁盘的读写性能,可能会致使 Android 系统越用越卡,而 Android 系统的 FSTRIM 对此有恢复的做用;
  2. 通过实验分析, FSTRIM 并不必定可以定期(天天一次)执行。而致使这一问题的缘由多是 IdleMaintenanceService 对电量的要求太高(未充电状态下大于80%)。

固然,咱们能够经过一下手段对这一问题作出优化尝试:

  1. FSTRIM 对电量的要求略高,如需提升其触发频率能够从下降触发条件中对电量的要求;
  2. 在必要的状况下,能够发送特定的 Intent 事件,使系统强制触发 FSTRIM。

至此,咱们也大体解答了项目组提出的问题,这个故事也基本能够告一段落了。

回顾整个分析问题的过程,我发现,做为一名专项测试人员,尽管我并不须要实际编写项目中的任何一句代码,但这并不意味着我不须要了解 Android 及其 Framework 的代码。实际上,只有在平时的学习和工做中了解其工做机制的基础上,咱们才能设计出合理的测试方案,从而更好 的完成工做。

腾讯 Bugly 是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的状况以及解决方案。智能合并功能帮助开发同窗把天天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同窗定位到出问题的代码行,实时上报能够在发布后快速的了解应用的质量状况,适配最新的 iOS, Android 官方操做系统,鹅厂的工程师都在使用,快来加入咱们吧…

相关文章
相关标签/搜索