本文来自于腾讯bugly开发者社区,非经做者赞成,请勿转载,原文地址:http://dev.qq.com/topic/578c93ca9644bd524bfcabe8git
“8小时内拼工做,8小时外拼成长”这是你们共同的理想。除了天天忙于工做外,咱们都但愿能更多地区吸取领域内的新知识与新技能,从而走向人生巅峰。github
Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是通过审核的移动开发工程师。每周都会举行嘉宾分享,话题讨论等活动。sql
上一期咱们邀请了腾讯SNG工程师“王少鸣”分享了《React Native项目实战总结》。数据库
本期,咱们邀请了腾讯WXG iOS开发工程师“姚海波”为你们分享《微信读书iOS性能优化》。
。
如何加入 Dev Club?缓存
移动端开发经验 >= 2 年,微信扫描下方群管理微信二维码,备注姓名-公司(或产品) 申请加入。安全
分享内容简介:
微信读书做为一款阅读类的新产品,目前还处于快速迭代,不断尝试的过程当中,性能问题也在业务的不断累积中逐渐体现出来。最近的1.3.0版本发布后,关于性能问题的用户反馈逐渐增多,为此,团队开始作一些针对性的性能问题优化。本次分享主要介绍咱们发现问题、解决问题和预防问题的历程。性能优化
内容的大致框架:微信
分享人介绍:markdown
姚海波 广州研发部 iOS开发工程师。网络
负责过的产品:QQ邮箱iOS客户端,目前主要负责微信读书iOS客户端的开发。
下面是本期分享内容整理
你们晚上好,我是来自广研的姚海波,你们能够叫我hypo。目前是微信读书项目中的iOS开发,主要负责阅读器相关的模块,还有APP总体性能优化方面的工做。
今天分享的内容是关于微信读书iOS开发过程当中,咱们解决性能问题的基本思路和方法,包括发现问题、解决问题和预防问题三个方面。
首先,根据我的的开发经验,我不得不认可,当应用发展到必定程度后,性能问题就不可能彻底避免。以往咱们老是但愿能寻找一种解决性能问题的一劳永逸的方法,实际上是不太现实的。因此咱们换个思路,如何尽早的发现性能问题,而后解决问题。
在发现问题方面,咱们项目也并无什么高招,主要有两个方面
用户反馈(包括测试人员)
受限于测试时间和用户反馈的积极性,性能问题每每到了比较严重的程度,开发人员才真正发现问题。
在线监控
在线监控主要有业务性能监控和卡顿监控
业务性能监控,主要在咱们认为很是关键的操做路径,例如:
卡顿监控,是用了Bugly的工具,而后经过动态下发开关,用抽样的方法进行上报
还有一些反馈卡顿的用户,咱们也会经过这个方法来查找问题
而后,在解决性能问题方法,相信你们都累积了不少经验。
产生性能问题的缘由多种多样,因此解决的办法也不尽相同,各类奇技淫巧都有可能派上用场,这里我大概介绍一下咱们项目中用到的一些方面:
1. 优化业务流程 2. 合理的线程分配 3. 预处理和延时加载 4. 缓存 5. 使用正确的API
性能优化看似高深,真正落到实处才会发现,最大的坑每每都隐藏在于业务不断累积和频繁变动之处。优化业务流程就是在知足需求的同时,提出更加高效优雅的解决方案,从根本上解决问题。从实践来看,这种方法解决问题是最完全的,但一般也是难度最大的。
这是咱们其中一个业务优化的案例
看似挺简单的优化,但真正落到实处,才会出现其中的坑有多大,因此重构优化的时候,还得有颗坚强的心!
因为GCD实在太方便了,若是不加控制,大部分须要抛到子线程操做都会被直接加到global队列,这样会致使两个问题:
合理的线程分配,最终目的就是保证主线程尽可能少的处理非UI操做,同时控制整个App的子线程数量在合理的范围内。
预处理,是将初次显示须要耗费大量线程时间的操做,提早放到后台线程进行计算,再将结果数据拿来显示。
延时加载,是指首先加载当前必须的可视内容,在稍后一段时间内或特定事件时,再触发其余内容的加载。这种方式能够颇有效的提高界面绘制速度,使体验更加流畅。(UITableView就是最典型的例子)
这两种方法都是在资源比较紧张的状况下,优先处理立刻要用到的数据,同时尽量提早加载即将要用到的数据。在微信读书中阅读的排版是优先级最高的,所在在阅读过程当中会预处理下一页、下一章的排版,同时可能会延时加载阅读相关的其它数据(如想法、划线、书签等)。
cache多是全部性能优化中最经常使用的手段,但也是咱们极不推荐的手段。cache创建的成本低,见效快,可是带来维护的成本却很高。若是必定要用,也请谨慎使用,并注意如下几点:
这方面主要仍是靠经验的累积
上面只是列举了几种常规手段,相信你们在实践过程当中,确定还有不少的高招。
通过一段时间的性能优化工做,咱们团队达成了一项共识,与其花那么时间去发现问题,查问题,还不如多开发一些工具,让问题尽可能暴露在开发阶段,最好达到避免共性问题。因此,咱们老是想开发一些有意思小工具来作这种事情。
下面列举几个咱们认识还挺有帮忙的工具:
1.内存泄露检测工具。 2.FPS/SQL性能监测工具条 3.UI/DataSource主线程检测工具 4.排版引擎自动化检测工具 5.书源检测工具
MLeakFinder,这个已经开源了,是咱们团队中zeposhe的杰做。
在此以前,内存泄露引发的性能问题是很难被察觉的,只有泄露到了至关严重的程度,而后经过Instrument工具,不断尝试才得以定位。MLeakFinder能在开发阶段,把内存泄露问题暴露无遗,减小了不少潜在的性能问题。
工具条是在DEBUG模式下,以浮窗的形式,实时展现当前可能存在问题的FPS次数和执行时间较长的SQL语句个数,是团队成员tower开发的。
FPS监测的原理并不复杂,虽然不是百分百准确,但很是实用,由于能够随时查看FPS低于某个阈值时的堆栈信息,再结合当时的使用场景,开发人员使用起来很是便利,能够很快定位到引发卡顿的场景和缘由。SQL语句的监测也很是实用,对于微信读书,DB的读写速度是影响性能的瓶颈之一。所以在DEBUG阶段,咱们监测了每一条SQL语句的执行速度,一旦执行时间超出某个阈值,就会表如今工具条的数字上,点击后能够进一步查询到具体的SQL操做以及实际耗时。
顶部工具条点击后,就能够查到具体是哪条sql语句慢
这个工具帮助咱们在开发阶段发现了不少卡顿问题,尤为是一些不合理的SQL语句,例如:
在想法圏的优化过程当中,利用这个工具,咱们就发现想法圈第一次加载更多,执行的SQL语句耗时居然达到了1000多毫秒。
_SELECT * FROM WRReview INNER JOIN WRUser ON WRReview.fromId = WRUser.vid WHERE WRReview.type & ? AND WRReview.createTime <= ? ORDER BY WRReview.createTime DESC , WRReview.itemId ASC LIMIT ?_
经过explain,能够发现这条SQL效率之低:
SEARCH TABLE WRReview SEARCH TABLE WRUser USING INTEGER PRIMARY KEY (rowid=?) USE TEMP B-TREE FOR ORDER BY
优化:给WRReview的 fromId createTime 两个字段增长了索引,并去掉一个排序字段:
_SELECT * FROM WRReview INNER JOIN WRUser ON WRReview.fromId = WRUser.vid WHERE WRReview.type & ? ORDER BY WRReview.createTime DESC LIMIT ?_
Explain的结果:
SCAN TABLE WRReview USING INDEX WRReview_createTime SEARCH TABLE WRUser USING INTEGER PRIMARY KEY (rowid=?)
该工具是为了保证全部的UI的操做和DataSource操做必定是在主线程进行。实现原理是经过hook UIView的setNeedsLayout,setNeedsDisplay,setNeedsDisplayInRect三个方法,确保它们都是在主线程执行。子线程操做UI可能会引发什么问题,苹果说得并不清楚,实际开发中咱们遇到几种神奇的问题彷佛都是跟这个有关。
app忽然丢动画,彷佛iOS系统也有这个bug。虽然没有确切的证据,但使用这个工具,改完全部的问题后,bug也好了(不止一次是这样)。
UI操做偶尔响应特别慢,从代码看没有任何耗时操做,只是简单的push某个controller。
莫名的crash,这固然是由于UI操做非线程安全引发的。
更多时候,子线程操做UI也并不必定会发生什么问题,也正由于不知道会发生什么,因此更须要咱们警戒,这个工具替咱们扫除了这些隐患。虽然,苹果表示,如今部分的UI操做也已是线程安全了,但毕竟大部分还不是。DataSource的监测是由于咱们业务定下的原则,保证列表DataSource的线程安全。
排版引擎是微信读书最核心的功能,排版引擎检测工具本来是为了检验排版引擎改进过程当中准确性,防止由于业务变动,而影响原来的排版特性。实现原理是结合自动化脚本和App自己的排版引擎,给书库中的每一本书创建一个镜像,镜像的内容包括书籍的每一章每一页的截图。而后分析同一页码的两个不一样版本的图片差别,就能够知道不一样版本的排版引擎渲染效果。可是我发现,只要稍加改进,排版后记录每一个章节排版耗时,就能够知道每一个版本变化后同一个章节的耗时变化,以此做为排版引擎的性能指标。这个工具保证了微信读书,即便在快速迭代过程当中也不会丢失阅读的核心体验。虽然这个工具没法在其它项目中复用,可是提醒了咱们,能够经过自动化工具来保证产品最核心功能的体验。
这个虽然业务相关性比较强,可是对于某些应用的自动化测试也是有效的
微信读书为了支持正版版权,目前书源彻底依赖于后台,不容许本地导入。书源的优劣的直接影响排版的效果和性能。为了解决了部分书籍没法打开或者乱码的问题,咱们借助了后台同窗的书源检测工具。对线上全部epub书籍(大概13,000本)进行扫描,按照章节大小进行排序。对于章节内容特别大的书籍重点检测,从新排版,解决了一批epub书籍没法打开的问题。同时针对章节内容乱码的问题,对全部txt的书籍进行了一次全量扫描,发现了一些问题,但还没法准确找出全部乱码的章节,这一点还在努力改善中。
经过上述介绍,咱们能够看出,性能问题广泛存在,无可避免,与其花费大量时间,查找线上版本的性能问题,不如提升总体团队成员性能优化意识,借助性能查找工具,将性能问题尽早暴露在开发阶段,达到预防为主的效果。
Q1:想问下大家 DB 操做这部分涉及到多线程读写是怎么处理的?
咱们用了FMDB,它已经处理了这种状况。
Q2:主线程检测工具备开源吗?
这个暂时没有开源,可是会推进开源。
Q3:除了 sqlite 语句的优化以外,db 这部分还有没有其余方面的优化工做?
https://github.com/Zepo/GYDataCenter
咱们有一个本身的DB框架,是ORM的,作了不少优化的工做,最近刚开源,你们能够看看。
Q4:请问大家选择用sqlite的考量是什么, 有没有考虑过使用其余的db如realm?
选择sqlite是历史缘由,由于咱们已经基于sqlite作了一个高性能的DB框架,并且也是通过QQMail App验证的。realm有考虑过,可是由于不是开源,因此估计不用采用。
Q5:FMDB 的解决方案,我理解是放到一个队列里,虽然能够解决多线程读写的问题,可是队列的处理仍是会阻塞住来自不一样线程的请求,对么?
是的。咱们一直也是读写都在同一条队列,其实并无太明显的性能瓶颈, 由于在sqlite之上咱们还有一层基于model的cache。
Q6:合理的使用线程,多线程之间的同步这块儿有什么方案或建议?
这里咱们也并无什么通用的方案,原则是尽可能避免使用多线程。必定要用的时候,也是根据业务谨慎选择。
或者咱们私下能够再具体讨论一些业务场景。
Q7:业务场景里会不会涉及到有读操做依赖写操做完成的状况,不然会出现读操做的数据不许确的状况。FMDB 感受不能很好的解决这个问题。
读操做 依赖写操做完成,这种场景必定会有的。可是这种问题应该是业务流程本身控制,而不是DB应该考虑的事情,DB性一能保证的就是按照业务提交的顺序,顺序执行。
Q8:能不能问下 微信读书的数据库的记录 通常是在什么级别,百、千?有没有尝试去作过一些压测,数据量达到多少的时候会遇到瓶颈?
微信读书的数据库记录并非很大,单表记录最多可能也就10w的数据级别。QQ邮箱的mailApp跟咱们是用的同一套,可是数量级别远大于微信读书。目前发现的瓶颈是DB文件达到200M以上时,sqlite的性能会明显受到影响,不过具体缘由还在调查中。有作过一些压力测试,用来对比CoreData,可是具体数据我这里暂时没有。
Q9:卡顿监控这块能详细说说么,用的是Bugly的哪一个工具呢,抽样上报具体是怎么样的?
Bugly库里有这个接口能够用+ (void)enableBlockMonitor:(BOOL)enable
另外再动态下发一个开关,设置这个值就行了。
Q10:微信读书这么成功,方便说下她的架构吗?我以为架构好才是她可优化的第一步。
哈哈,如今还远谈不上成功啦。架构要用图来画才方便看,我暂时还没总结整个app的架构, 能够看看关于阅读器epub渲染的一个架构。
Q11:sql对于版本升级时表结构发生变化时如何处理?特别是跨版本升级!
https://github.com/Zepo/GYDataCenter 这个是基本ORM的一个框架,会自动把model和sqlite表的字段作一个映射,升级的时候,若是发现sqlite缺乏的字段,会自动建立。可是,由于sqlite不能修改字段,因此咱们也只能用于新增字段。
Q12:大家的 db 是只有一个文件,仍是尝试分文件存储的?
看业务需求,目前是多个DB文件。
更多精彩内容欢迎关注bugly的微信公众帐号:
腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的状况以及解决方案。智能合并功能帮助开发同窗把天天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同窗定位到出问题的代码行,实时上报能够在发布后快速的了解应用的质量状况,适配最新的 iOS, Android 官方操做系统,鹅厂的工程师都在使用,快来加入咱们吧!