美文网首页
APP的性能监控方案:CPU、FPS、内存

APP的性能监控方案:CPU、FPS、内存

作者: 拧发条鸟xds | 来源:发表于2022-05-17 11:44 被阅读0次

    线下监控

    • Xcode Instruments
    • 使用 Instruments 的 os_signpost API 来完成自定义的性能数据监控工具开发。

    线上监控

    原则

    1. 监控代码不要侵入到业务代码中;
    2. 采用性能消耗最小的监控方案。

    监控内容

    • 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;
    }
    

    相关文章

      网友评论

          本文标题:APP的性能监控方案:CPU、FPS、内存

          本文链接:https://www.haomeiwen.com/subject/fpxnurtx.html