卡顿监控设计
背景
卡顿这个性能异常,对用户体验伤害非常大,UI卡死,直接导致用户对于整个APP的使用性及可靠性打上一个大大的问号。在移动设备上开发应用,性能是每一个RD关心的问题。作为RD我们可以通过提高自身技术,提高代码质量来优化应用的性能。当然我们不一定能及时发现代码存在的性能问题,这时就需要有一个能够监控并及时告知我们应用中那些耗费性能导致应用卡顿的“Bug”。
卡顿原因:
App的UI操作是在主线程执行,而且app中很多系统开放的API也是在主线程中调用。这就造成了程序员很容易将一些IO,计算,绘图等比较耗时的操作放到主线程执行,从而造成主线程不能流畅的执行UI更新,造成卡顿现象。
另外一种造成卡顿的原因是执行一些大规模的计算(相对于移动设备而言),虽然这些计算没有在主线程执行,但是却造成CPU爆表,CPU切片到主线程的次数变少,也会造成主线程卡顿
设计目标
卡顿监控能区分组件维度
卡顿监控是可控的,阈值可调节
卡顿监控的性能损耗要在可控范围
端卡顿监控层技术方案
Android端可以直接获取ANR日志,或者通过ping主线程根据消息响应速度判断是否卡顿。监控到卡顿,捞取堆栈上传卡顿现场
iOS比较麻烦。有2种方案提供选择
Runloop方案:传统方案。子线程监控主线程Runloop Activity,由于Runloop真正执行事件是kCFRunLoopBeforeSources和kCFRunLoopBeforeWaiting之间,所以可以记录activity处于这两个activity之间的时间,超过设定的卡顿阈值时即为发现卡顿。有兴趣的可以看老谭的《iOS实时卡顿监控》(http://www.tanhao.me/code/151113.html/)
Ping方案:创新性方案。子线程通过信号量去ping主线程,因为ping的时候主线程肯定是在kCFRunLoopBeforeSources和kCFRunLoopBeforeWaiting之间。信号量来控制阈值,ping不通的话由于ping操作没有被主线程执行,所以也不会污染主线程堆栈。
iOS卡顿监控方案对比
性能
Ping方案能有效控制ping主线程的频率,主线程上执行的代码可以有效控制频率。
Runloop方案,主线程监控回调会一直高频率调用,有损性能(除非主线程没有任何操作,但是对于滴滴出行这样的大型企业级app而言基本是不可能的)。
2. Ping方案在主线程中调用的就两行代码。
Runloop方案在主线程调用的回调中有4行代码。对于高频率的调用也是一种损耗。
可靠性
Ping方案检测到block时不会执行dispatch_async(dispatch_get_main_queue()中的代码,不会污染主线程堆栈。
Runloop方案,由于主线程监控回调会一直高频率调用,当检测到卡顿的时候监控回调中的代码已经执行,会污染主线程堆栈,导致dump出的堆栈被污染,降低dump出的堆栈的可靠性。
可控性
Ping方案卡顿监控能提供pause,resume,stop等方法
Runloop方案没有pause,resume功能。stop也只能把整个CFRunLoopObserverRef释放掉,而且主线程监控回调会高频率的调用,相对有损性能。
综上所述,iOS卡顿监控决定采用创新性的Ping方案。
原理详解:我们编写一个继承自NSThread的叫ONEPingThread类,重写main方法,在main方法中去ping主线程,然后通过信号量来控制ping的节奏。
卡顿监控设计
背景
卡顿这个性能异常,对用户体验伤害非常大,UI卡死,直接导致用户对于整个APP的使用性及可靠性打上一个大大的问号。在移动设备上开发应用,性能是每一个RD关心的问题。作为RD我们可以通过提高自身技术,提高代码质量来优化应用的性能。当然我们不一定能及时发现代码存在的性能问题,这时就需要有一个能够监控并及时告知我们应用中那些耗费性能导致应用卡顿的“Bug”。
卡顿原因:
App的UI操作是在主线程执行,而且app中很多系统开放的API也是在主线程中调用。这就造成了程序员很容易将一些IO,计算,绘图等比较耗时的操作放到主线程执行,从而造成主线程不能流畅的执行UI更新,造成卡顿现象。
另外一种造成卡顿的原因是执行一些大规模的计算(相对于移动设备而言),虽然这些计算没有在主线程执行,但是却造成CPU爆表,CPU切片到主线程的次数变少,也会造成主线程卡顿
设计目标
卡顿监控能区分组件维度
卡顿监控是可控的,阈值可调节
卡顿监控的性能损耗要在可控范围
端卡顿监控层技术方案
Android端可以直接获取ANR日志,或者通过ping主线程根据消息响应速度判断是否卡顿。监控到卡顿,捞取堆栈上传卡顿现场
iOS比较麻烦。有2种方案提供选择
Runloop方案:传统方案。子线程监控主线程Runloop Activity,由于Runloop真正执行事件是kCFRunLoopBeforeSources和kCFRunLoopBeforeWaiting之间,所以可以记录activity处于这两个activity之间的时间,超过设定的卡顿阈值时即为发现卡顿。有兴趣的可以看老谭的《iOS实时卡顿监控》(http://www.tanhao.me/code/151113.html/)
Ping方案:创新性方案。子线程通过信号量去ping主线程,因为ping的时候主线程肯定是在kCFRunLoopBeforeSources和kCFRunLoopBeforeWaiting之间。信号量来控制阈值,ping不通的话由于ping操作没有被主线程执行,所以也不会污染主线程堆栈。
iOS卡顿监控方案对比
性能
Ping方案能有效控制ping主线程的频率,主线程上执行的代码可以有效控制频率。
Runloop方案,主线程监控回调会一直高频率调用,有损性能(除非主线程没有任何操作,但是对于滴滴出行这样的大型企业级app而言基本是不可能的)。
2. Ping方案在主线程中调用的就两行代码。
Runloop方案在主线程调用的回调中有4行代码。对于高频率的调用也是一种损耗。
可靠性
Ping方案检测到block时不会执行dispatch_async(dispatch_get_main_queue()中的代码,不会污染主线程堆栈。
Runloop方案,由于主线程监控回调会一直高频率调用,当检测到卡顿的时候监控回调中的代码已经执行,会污染主线程堆栈,导致dump出的堆栈被污染,降低dump出的堆栈的可靠性。
可控性
Ping方案卡顿监控能提供pause,resume,stop等方法
Runloop方案没有pause,resume功能。stop也只能把整个CFRunLoopObserverRef释放掉,而且主线程监控回调会高频率的调用,相对有损性能。
综上所述,iOS卡顿监控决定采用创新性的Ping方案。
原理详解:我们编写一个继承自NSThread的叫ONEPingThread类,重写main方法,在main方法中去ping主线程,然后通过信号量来控制ping的节奏。