线下监控
- Xcode Instruments
- 使用 Instruments 的 os_signpost API 来完成自定义的性能数据监控工具开发。
线上监控
原则
- 监控代码不要侵入到业务代码中;
- 采用性能消耗最小的监控方案。
监控内容
- CPU使用率
- FPS帧率
- 内存
CPU使用率
方案
APP对CPU使用率,等于APP中所有线程对CPU使用率的总和。
在iOS系统中,usr/include/mach/thread_info.h 里可以看到线程基本信息的结构体:
struct thread_basic_info {
time_value_t user_time; // 用户运行时长
time_value_t system_time; // 系统运行时长
integer_t cpu_usage; // CPU 使用率
policy_t policy; // 调度策略
integer_t run_state; // 运行状态
integer_t flags; // 各种标记
integer_t suspend_count; // 暂停线程的计数
integer_t sleep_time; // 休眠的时间
};
实现
使用定时器,每2s去遍历所有线程,累加线程的cpu_usage字段的值,获得当前APP对CPU的使用率。
+ (integer_t)cpuUsage {
thread_act_array_t threads; //int 组成的数组比如 thread[1] = 5635
mach_msg_type_number_t threadCount = 0; //mach_msg_type_number_t 是 int 类型
const task_t thisTask = mach_task_self();
//根据当前 task 获取所有线程
kern_return_t kr = task_threads(thisTask, &threads, &threadCount);
if (kr != KERN_SUCCESS) {
return 0;
}
integer_t cpuUsage = 0;
// 遍历所有线程
for (int i = 0; i < threadCount; i++) {
thread_info_data_t threadInfo;
thread_basic_info_t threadBaseInfo;
mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) {
// 获取 CPU 使用率
threadBaseInfo = (thread_basic_info_t)threadInfo;
if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {
cpuUsage += threadBaseInfo->cpu_usage;
}
}
}
assert(vm_deallocate(mach_task_self(), (vm_address_t)threads, threadCount * sizeof(thread_t)) == KERN_SUCCESS);
return cpuUsage;
}
FPS监控
通过注册 CADisplayLink 得到屏幕的同步刷新率,记录每次刷新时间,然后就可以得到 FPS。
- (void)start {
self.dLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsCount:)];
[self.dLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
// 方法执行帧率和屏幕刷新率保持一致
- (void)fpsCount:(CADisplayLink *)displayLink {
if (lastTimeStamp == 0) {
lastTimeStamp = self.dLink.timestamp;
} else {
total++;
// 开始渲染时间与上次渲染时间差值
NSTimeInterval useTime = self.dLink.timestamp - lastTimeStamp;
if (useTime < 1) return;
lastTimeStamp = self.dLink.timestamp;
// fps 计算
fps = total / useTime;
total = 0;
}
}
内存监控
WWDC:iOS Memory Deep Dive
phys_footprint是实际使用的物理内存。
内存信息存在task_info.h(完整路径 usr/include/mach/task.info.h)文件的task_vm_info结构体中,其中phys_footprint就是物理内存的使用,而不是驻留内存resident_size。结构体里和内存相关的代码如下:
struct task_vm_info {
mach_vm_size_t virtual_size; // 虚拟内存大小
integer_t region_count; // 内存区域的数量
integer_t page_size;
mach_vm_size_t resident_size; // 驻留内存大小
mach_vm_size_t resident_size_peak; // 驻留内存峰值
...
/* added for rev1 */
mach_vm_size_t phys_footprint; // 物理内存
...
类似于对 CPU 使用率的监控,我们只要从这个结构体里取出 phys_footprint 字段的值,就能够监控到实际物理内存的使用情况了。
uint64_t memoryUsage() {
task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if (result != KERN_SUCCESS)
return 0;
return vmInfo.phys_footprint;
}