美文网首页
iOS 性能探究

iOS 性能探究

作者: 2020silence | 来源:发表于2019-09-26 19:53 被阅读0次

    一、什么是 App性能?性能指标是什么?

    一般来讲,性能问题虽然不会导致 App不可用,但会影响用户体验,用户只关心产品体验问题,如果说性能问题不断累计达到临界点,就会爆发出一些不可估计的问题,要么任性地卡顿运行,要么就低调地崩溃,最后导致用户用着不开心,开发者也比较烦恼,如果是这样的话,你在这家公司的可能就呆不长时间了。
    性能指标:主要集中在 CPU 使用率、FPS 的帧率和内存这三个方面

    二、为什么需要考虑 App性能呢?

    做任何一件事情,我们都必须追求完美,做开发也是一样的,我们都需要站在用户角度和公司利益出发去考虑问题,去工作,去打造一款在市场上能站稳脚的 App。一款好的 App,不仅仅是要抓住了用户需求,还需要完美的体验,视觉的冲击,得让用户用起来舒服,有种爱不释手的感觉,“阿哈”的感觉。(“阿哈”解释于《黑客增量》一书中)
    把自己往大牛的路上逼,我要当大牛

    三、如何对 App做性能监测?

    为了主动、高效地发现性能问题,避免 App 质量进入无人监管的失控状态,我们需要对App 的性能进行监控,目前,对 App 的性能监控,主要是分为线下和线上两个方面。

    线下性能监控:Instruments

    Instruments是由苹果官方力推的唯一一款线下性能监控,性能分析工具,集成于xcode,功能非常强大,比如说Leaks专门用于监控内存泄露问题的,Network专门用于检查网络情况的,Time Profiler对程序命令执行时间的采样来分析页面卡顿问题的,等等......

    • Instruments 可以实现下面功能:
      1.分析一个或多个进程的行为。
      2.记录一系列用户的动作并响应它们,可靠的再现这些事件并收集多次运行的数据。
      3.创建你自己自定义的 DTrace instruments 来分析系统和应用程序的行为。
      4.保存用户界面记录和instruments的配置为模板,并从Xcode里面访问。
      5.追查代码中难以重现的问题。
      6.对你的程序进行性能分析。
      7.自动化测试你的代码。
      8.对你程序进行压力测试。
      9.进行一般的系统级故障诊断。
      10.对你的代码如何工作有更深入的了解。

    • 启动Instruments:


      image.png

      启动后的界面:


      启动后的界面.png
    • Instruments创建跟踪文档:
      1.Blank:创建一个空的模板,你可以自定义的添加各种工具。
      2.Activity Monitor:可以只用这个模板,研究系统工作负载和虚拟内存大小的关系
      3.Allocations:将Allocations和VM跟踪器加到跟踪文档中,使用该工具可以监视内存和对象的内存分配方式和情况。
      4.CocoaLayout:是一种Cocoa布局工具 ,可以应用于iOS模拟器和Cocoa桌面应用,但是不能和连接的iOS设备一起使用。观察NSLayoutConstraint对象的改变,帮助我们判断什么时间什么地点的constraint是否合理
      5.Core Animation:将CoreAnimation加入到跟踪文档中,可以测量ios设备上每秒的CoreAnimation帧数,这可以帮助你理解内容是如何渲染到屏幕上的
      6.CoreData:将CoreData数据提取、缓存缺失和存储加入到跟踪文档中,使用这个工具可以检测应用程序中数据的存储交互。
      7.Counters:收集使用时间或基于事件的抽样方法的性能监控计数器(PMC)事件。
      8.Energy Log:耗电量监控,将Energy Diagnostics, CPU Activity, Display Brightness, Sleep/Wake, Bluetooth, WiFi, and GPS instruments加入到跟踪文档中进行检测。
      9.File Activity:将File Activity, Reads/Writes, File Attributes, and Directory I/O instruments 加入到跟踪文档中,只用这个模板可以让你检查系统文件的使用情况,可以检查文件的打开、关闭、读和写操作,同时也可以检测文件系统本身的改变,包括权限和所有权发生的改变。
      10.Leaks:将the Allocations and Leaks instruments加入到模板中,使用这个模板可以帮助你检测内存的泄漏。
      11.Metal System Trace:它是是apple 2014年在ios平台上推出的高效底层的3D图形API,它通过减少驱动层的API调用CPU的消耗提高渲染效率。
      12.Network:用链接工具分析你的程序如何使用TCP/IP和UDP/IP链接。
      13.OpenGL ES Analysis:将OpenGL ES Analyzer and OpenGL ES Driver加入到模板中,这个模块测量分析OpenGL ES活动正确性检测以及表现问题,提供解决建议。
      14.System Trace:系统跟踪,通过显示当前被调度线程提供综合的系统表现,显示从用户到系统的转换代码通过两个系统调用或内存操作。
      15.System Usage:这个模板记录关于文件读写,sockets,I/O系统活动, 输入输出。
      16.Time Profile:执行对系统的CPU上运行的进程低负载时间为基础采样。
      17.Zombies:测量一般的内存使用,专注于检测过度释放的野指针对象,也提供对象分配统计,以及主动分配的内存地址历史。

    • 跟踪文档举例:如Leaks


      Leaks

      Leaks图中数字说明说明:
      1.开始录制/暂停
      2.call tree的官方解释:Separate By Thread(线程分离,只有这样才能在调用路径中能够清晰看到占用CPU最大的线程.)
      3.Invert Call Tree 的意思是翻转调用树,直接定位到内存泄漏的那个方法,如果不勾选会显示最外层的函数,我们还要手动展开。可以点选看下效果
      Hide System Libraries 的意思很明显了。就是隐藏系统类库,避免一些莫名其妙的,我们无法改动的信息迷惑我们。
      4.刷新间隔时间
      5.双击第三行定位到具体泄漏的函数,泄漏的字节数都标出来了,如此强大,定位到泄漏代码,加以更改后就可以了
      如下图举例:


      image.png
    • 功能简介:


      Instruments功能简介图

    其中常用的有以下几种工具:

    Allocations,
    Leaks,
    Zombies,
    Core Animation,
    Automation,
    Time Profiler,
    Cocoa Layout,
    Energy Diagnostics,
    Network
    
    • 自定义Instruments
      除了对各种性能问题进行监控外,最新版本的 Instruments 10 还有以下两大优势:
      1.Instruments 基于 os_signpost 架构,可以支持所有平台。
      2.Instruments 由于标准界面(Standard UI)和分析核心(Analysis Core)技术,使得我们可以非常方便地进行自定义性能监测工具的开发。当你想要给 Instruments 内置的工具换个交互界面,或者新创建一个工具的时候,都可以通过自定义工具这个功能来实现。

    开发一款自定义 Instruments 工具,主要包括以下这几个步骤:
    a.在 Xcode 中,点击 File > New > Project;
    b.在弹出的 Project 模板选择界面,将其设置为 macOS;
    c.选择 Instruments Package,点击后即可开始自定义工具的开发了
    点击查看详情

    线上性能监控:CPU 使用率、FPS 的帧率和内存

    需要考虑两个原则:
    a.监控代码不要侵入到业务代码中。
    b.采用性能消耗最小的监控方案。

    CPU 使用率的线上监控方法

    App 作为进程运行起来后会有多个线程,每个线程对 CPU 的使用率不同。各个线程对 CPU 使用率的总和,就是当前 App 对 CPU 的使用率。明白了这一点以后,我们也就摸清楚了对 CPU 使用率进行线上监控的思路。

    在 iOS 系统中,你可以在 usr/include/mach/thread_info.h 里看到线程基本信息的结构体,其中的 cpu_usage 就是 CPU 使用率。结构体的完整代码如下所示:

    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; // 休眠的时间
    };
    

    因为每个线程都会有这个 thread_basic_info 结构体,所以接下来的事情就好办了,你只需要定时(比如,将定时间隔设置为 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;
    }
    

    在上面这段代码中,task_threads 方法能够取到当前进程中的线程总数 threadCount 和所有线程的数组 threads。
    接下来,我们就可以通过遍历这个数组来获取单个线程的基本信息。其中,线程基本信息的结构体是 thread_basic_info_t,这个结构体里就包含了我们需要的 CPU 使用率的字段 cpu_usage。然后,我们累加这个字段就能够获取到当前的整体 CPU 使用率。
    到此,我们就实现了对 CPU 使用率的线上监控。接下来,我们再看看对 FPS 的线上监控方法吧。

    FPS 线上监控方法

    FPS 是指图像连续在显示设备上出现的频率。FPS 低,表示 App 不够流畅,还需要进行优化。
    但是,和前面对 CPU 使用率和内存使用量的监控不同,iOS 系统中没有一个专门的结构体,用来记录与 FPS 相关的数据。但是,对 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;
        }
    }
    
    内存使用量的线上监控方法

    通常情况下,我们在获取 iOS 应用内存使用量时,都是使用 task_basic_info 里的 resident_size 字段信息。但是,我们发现这样获得的内存使用量和 Instruments 里看到的相差很大。后来,在 2018 WWDC Session 416 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;  // 物理内存
    }
    

    OK,类似于对 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 本身的性能影响也非常小,满足了我们要考虑的两个原则。

    说明:
    本文内从不做任何商业用途,仅仅本人学习笔记记录,方便以后回顾

    版权参考文章:
    极客空间
    简书

    相关文章

      网友评论

          本文标题:iOS 性能探究

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