性能优化:屏幕卡顿优化

2019-10-18  本文已影响0人  意一ineyee
一、屏幕成像原理及屏幕卡顿原因
二、屏幕卡顿优化
三、定量监测屏幕FPS
四、定位卡顿效果
五、定位耗时代码
六、果然好客服功能,聊天界面滑动优化

一、屏幕成像原理及屏幕卡顿原因


屏幕成像原理:iOS设备的屏幕成像,是由CPU和GPU协作完成的。CPU(中央处理器)主要负责计算要显示的内容

而GPU(图形处理器)主要负责渲染要显示的内容。

那么当显示器想要显示内容时,就会发出一个VSync(垂直同步信号,iOS设备每秒会发出60个VSync——即iOS设备的屏幕刷新频率为60Hz),系统会立马开始让CPU计算要显示的内容,计算完后,把计算结果发送给GPU;GPU渲染要显示的内容,渲染完后,把渲染结果缓存到帧缓存中;等下一个VSync到来时,CPU计算、GPU渲染下一帧的同时,视频控制器会从帧缓存中读取一帧的渲染结果显示到屏幕上。

屏幕卡顿原因:所以一旦下一个VSync到来时,因为CPU或GPU的压力过大,没有及时渲染完下一帧缓存到帧缓存中,那么屏幕就会继续显示上一帧,这就导致屏幕该刷新的时候没刷新,出现滞留现象——这就是我们常说的屏幕卡顿或者屏幕掉帧。

二、屏幕卡顿优化


所以我们做屏幕卡顿优化,主要就是从减轻CPU和GPU的压力入手,尽可能地保证在下一个VSync到来时,CPU和GPU能够协作完成下一帧的渲染并缓存到了帧缓存中。

1、视图方面

2、文本、图片方面

3、耗时代码方面

4、尽可能避免离屏渲染

4.1 离屏渲染是什么?

On-Screen Rendering:在屏渲染,即GPU的渲染结果直接存储在帧缓存中供下一帧显示。

Off-Screen Rendering离屏渲染,即GPU的渲染结果无法直接存储在帧缓存中时,系统就会调用CPU开辟一块新缓存来存储渲染结果。

4.2 为什么要尽可能避免离屏渲染?

正常情况下就是在屏渲染,即GPU每渲染一帧就缓存到帧缓存中供下一帧显示。但在某些情况下,GPU的渲染结果无法直接存储在帧缓存中,于是系统就会调用CPU开辟一块新缓存来存储渲染结果。这样视频控制器要读取离屏渲染缓存时就要把上下文切换到新缓存去读取,要读取在屏渲染缓存时又要把上下文切换到帧缓存去读取,这样反复的上下文切换非常消耗CPU。

所以从开辟新缓存和反复切换上下文这两点来看,我们就知道离屏渲染很有可能导致CPU计算、GPU渲染超过1/60s,从而导致屏幕卡顿,所以要尽可能避免离屏渲染。

4.2 什么情况下会触发离屏渲染?

三、定量监测屏幕FPS


屏幕FPS的理想值为60,也就是说当屏幕一秒刷新60次时,用户滑动界面是感觉不到卡顿的。而根据苹果官方的说法,当屏幕FPS低于45时,用户滑动界面就会感觉到明显的卡顿。

InstrumentsCore Animation
// ViewController.m
@interface ViewController () <UITableViewDataSource, UITableViewDelegate>

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
    tableView.backgroundColor = [UIColor whiteColor];
    tableView.dataSource = self;
    tableView.delegate = self;
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cellReuseID"];
    [self.view addSubview:tableView];
}


#pragma mark - UITableViewDataSource, UITableViewDelegate

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return 100;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellReuseID" forIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"%ld", indexPath.row];
    
    return cell;
}

@end
FPS接近60,很流畅

我们在cellForRowAtIndexPath方法里添加一些耗时操作。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellReuseID" forIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"%ld", indexPath.row];
    
    // 模拟耗时操作
    for (int i = 0; i < 2000; i++) {
    
        NSLog(@"%d", i);
    }
    
    return cell;
}
FPS陡然降低,会感觉到明显的卡顿

四、定位卡顿效果


打开Xcode,把项目运行在真机上,然后可以用下面常用的属性去定位卡顿效果。

五、定位耗时代码


此处推荐一个三方库LXDAppFluecyMonitor来定位耗时代码。

六、果然好客服功能,聊天界面滑动优化


当然屏幕滑动优化的方案有很多,比如尽量减少透明视图的使用;尽量让图片的大小和UIImageView一样大,减少图片伸缩;尽量减少切圆角导致的离屏渲染;尽量把一些耗时操作放到子线程中,如文本宽高的计算等。

但是经过定量检测,我们发现影响我们聊天界面FPS的主要原因就是一个,那就是“滑动界面时,会一直计算聊天文本Label的高度和聊天气泡Cell的高度”,其它三个原因对聊天界面FPS的影响微乎其微。

所以我们就定了主要的优化方案就是针对耗时操作,优化之前聊天界面的FPS在53左右,而优化之后FPS就变成了57~59。具体的做法就是,请求完聊天数据后,我们会立马开辟一个子线程去计算聊天文本的高度,这样也计算出了每个Cell的高度,把它们和聊天数据一起封装成一个Cell布局对象。这样TableView在频繁地调用heightForRowAtIndexPath方法时,就不用一直计算Cell的高度了,同时把这个Cell布局对象设置到Cell内部时,Cell内部也不用一直计算聊天内容Label的高度了,这可以很大程度地提升聊天界面的FPS。

上一篇 下一篇

猜你喜欢

热点阅读