性能优化,都要优化什么?
- 启动时间
- 内存
- 刷新帧率
- UI阻塞次数,不可操作时长,主线程阻塞超过400毫秒次数
- CPU使用率
- GPU使用率
- 网络请求时间,流量消耗
- 耗电功率
如何去监测这些性能指标。
-
Xcode自带的Instrument Instrument 用户指南(中文版)
Instrument 性能检测图谱:
使用第三方SDK :Bugly、OneAPM、听云、Firebase Analytics
自行开发检测代码
启动时间
1.通过Instrument的Time Profiler,找到包含-[UIApplication _reportAppLaunchFinished]的最后一帧,可计算出启动时间。
2.自行添加代码计算
t1
系统dylib和自身app可执行文件(app中所有.o文件的集合)的加载
Launch页
t2
main()
UIApplicationMain()
willFinishLaunchingWithOptions()
didFinishLaunchingWithOptions()
t3
loadView()
viewDidLoad()
applicationDidBecomeActive()
启动时间 t = t1 + t2;
app从启动到打开第一个页面的时间 t = t1 + t2 + t3;
t1:在Xcode的Edit scheme中增加
DYLD_PRINT_STATISTICS这个环境变量,控制台就会打印这个时间。
t2、t3都可以通过埋点的方式添加获取时间的代码即可。
内存
目前在开发的阶段只会注意内存泄露和内存异常
两种情况,具体的内存分配、僵尸对象等可能会在开发结束后进行检测。
内存泄露
除了使用instrument,还有:
- 手动添加代码:
- (void)delloc {
NSLog(@"------------->--->%@被释放了",[self class]);
}
这个只是说明某个类释放情况,并不能定位到具体代码,需要在类内部进行排查。
- 第三方工具MLeaksFinder,优点:
1.这个库不需要入侵项目代码,直接pod导入一下库就
2.不用像instruments那么麻烦,还需要自己来仔细观察
3.库只在debug状态运行,完全不影响app打包大小
内存异常
就只是观察Xcode里面的内存显示或者埋点插入内存计算的方法,代码如下:
#import <mach/mach.h>
#import <mach/task_info.h>
- (unsigned long)memoryUsage
{
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kr != KERN_SUCCESS) {
return -1;
}
unsigned long memorySize = info.resident_size >> 10;//10-KB 20-MB
return memorySize;
//返回的数值单位是KB,如果想要MB的话把10改为20。
}
刷新帧率
刷新帧率利用CADisplayLink做一个小工具进行检测,查看认识并使用CADisplayLink。
UI阻塞次数,不可操作时长,主线程阻塞超过400毫秒次数
美团移动端性能监控方案Hertz
在实践中采用的是检测主线程每次执行消息循环的时间,当这一时间大于阈值时,就记为发生一次卡顿。
开辟一个子线程,然后实时计算 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting 两个状态区域之间的耗时是否超过某个阀值,来断定主线程的卡顿情况。这个还是比较准确的数据。
但是由于主线程的RunLoop在闲置时基本处于Before Waiting状态,这就导致了即便没有发生任何卡顿,这种检测方式也总能认定主线程处在卡顿状态。
CPU使用率
CPU使用率只需观察Xcode里面的数据或者埋点插入CPU使用率计算的方法,代码如下:
- (float)cpu_usage
{
kern_return_t kr = { 0 };
task_info_data_t tinfo = { 0 };
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
kr = task_info( mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count );
if ( KERN_SUCCESS != kr )
return 0.0f;
task_basic_info_t basic_info = { 0 };
thread_array_t thread_list = { 0 };
mach_msg_type_number_t thread_count = { 0 };
thread_info_data_t thinfo = { 0 };
thread_basic_info_t basic_info_th = { 0 };
basic_info = (task_basic_info_t)tinfo; // get threads in the task
kr = task_threads( mach_task_self(), &thread_list, &thread_count );
if ( KERN_SUCCESS != kr )
return 0.0f;
long tot_sec = 0;
long tot_usec = 0;
float tot_cpu = 0;
for ( int i = 0; i < thread_count; i++ )
{
mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX;
kr = thread_info( thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count );
if ( KERN_SUCCESS != kr )
return 0.0f;
basic_info_th = (thread_basic_info_t)thinfo;
if ( 0 == (basic_info_th->flags & TH_FLAGS_IDLE) )
{
tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds;
tot_usec = tot_usec + basic_info_th->system_time.microseconds + basic_info_th->system_time.microseconds;
tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE;
}
}
kr = vm_deallocate( mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t) );
if ( KERN_SUCCESS != kr )
return 0.0f;
return tot_cpu * 100.; // CPU 占用百分比}
具体使用可以使用instrument查看。
GPU使用率
iOS 设备跟踪 GPU 使用率
GPUUtilization GPU使用率检测Demo
网络请求时间,流量消耗
该部分在AFNetworking研究之后再做补充。
链接文章:
iOS APP性能检测&优化(二)