性能分析工具——os_signpost

作者: 小虾米1309 | 来源:发表于2019-07-03 16:07 被阅读0次

    1. os_signpost是什么

    signpost这个单词是路标、指示牌的意思,比如距离北京还有158km。顾名思义,os_signpost就是在代码里面插入一些标记,作用相当于路标。官方文档是这样描述的:

    The os_signpost APIs let clients add lightweight instrumentation to
    code for collection and visualization by performance analysis tooling.

    用人话说就是:os_signpost是一个轻量级的可视化的性能分析工具。

    2. os_signpost的提出背景

    代码层面的性能分析,最直观的方式就是标识出一段代码的开始和结尾,然后计算下耗时。就像下面这样:

    CFTimeInterval begin = CACurrentMediaTime();
    // do something 
    CFTimeInterval end = CACurrentMediaTime();
    NSLog(@"cost = %@",(end - begin));
    

    然而如果代码逻辑复杂、有先后关系、存在多个线程等,单靠某小段代码的标记,就不是那么直观了。

    于是,在2018年9月,苹果推出了os_signpost,它可以配合Instruments,显示可视化的效果,WWDC视频Measuring Performance Using Logging有介绍。因为是18年才推出,所以os_signpost只支持iOS12及以上系统,Xcode10及更高版本。

    3. os_signpost的用法

    这里以OC代码为例。

    (1) 先导入头文件,为了使用方便,再定义两个宏:
    #include <os/signpost.h>
    
    #define INNER_BEGIN_LOG(subsystem, category, name) \
    os_log_t m_log_##name = os_log_create((#subsystem), (#category));\
    os_signpost_id_t m_spid_##name = os_signpost_id_generate(m_log_##name);\
    os_signpost_interval_begin(m_log_##name, m_spid_##name, (#name));
    
    #define INNER_END_LOG(name) \
    os_signpost_interval_end(m_log_##name, m_spid_##name, (#name));
    

    这两个宏是成对使用的,这段代码可以放公共的头文件里,方便不同的地方用。

    (2)给代码插入路标:

    页面初始化的时候插入名字为init的标记,
    viewDidAppear的时候插入名字为viewDidAppear的标记,
    接口请求回调时插入名字为requestDidCompleted的标记,
    数据处理完插入名字为requestProcessComplete的标记。

    - (id)init {
        self = [super init];
        if (self) {
            if (@available(iOS 12.0, *)) {
                INNER_BEGIN_LOG(fourPage, init, init);
                INNER_END_LOG(init);
            }
            // do something
        }
        return self;
    }
    
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        // do something
        if (@available(iOS 12.0, *)) {
            INNER_BEGIN_LOG(fourPage, init, viewDidAppear);
            INNER_END_LOG(viewDidAppear);
        }
    }
    
    - (void)requestDidCompleted {
        if (@available(iOS 12.0, *)) {
            INNER_BEGIN_LOG(fourPage, init, requestDidCompleted);
            INNER_END_LOG(requestDidCompleted);
        }
        // do something
        if (@available(iOS 12.0, *)) {
            INNER_BEGIN_LOG(fourPage, init, requestProcessComplete);
            INNER_END_LOG(requestProcessComplete);
        }
    }
    
    (3)工程设置

    Debug Information Format设置为DWARF with dSYM File,如下图所示:

    工程设置.png
    这一步是为了后面在Instruments里面能直接看到代码。
    现在就可以插上手机跑起来了。
    (4)配合Instruments
    打开Instruments的Time Profiler,默认会有一行Points of Interest,这里用不到,可以把它删了,选中再按删除键即可。然后添加os_signpost,如下图所示: 添加os_signpost.png

    把这个模板保存起来,点上面的File->Save As Template...,输入名字signpost。

    下次就可以直接从Instruments里打开这个自定义模板了: 自定义模板.png
    选择刚刚跑的app,点左上角的红色按钮开始录制,这时候界面显示Recording,操作完了再次点左上角按钮结束录制,然后进入下面这样的界面: Instruments_1.png
    刚录制完可能看不到刚刚插入的那些标记,只要在上图中红线位置往下拖,把界面展开就看到了,如下图所示。左边红框就是添加的4个标记,右边红框里的竖线是相应的时间。把鼠标放在右边红框位置,滚动滚轮,可以缩放时间轴,笔记本用户可以用触摸板缩放。
    Instruments_2.png

    时间轴放大后可以清晰地看到,从init到viewDidApper整个过程耗时840ms,其中接口请求耗时405ms,接口解析耗时35ms,页面渲染耗时400ms,还可以看到这段时间主线程压力很大。

    到这就可以分析一下具体哪些代码耗时了。在下图1位置选Samples,就是对代码采样,在2位置消耗CPU较严重的区域点击,然后在3位置就可以看到该位置对应的代码,如果前面Debug Information Format没有设置的话,这里是看不到代码的。 Instruments_3.png
    以我的代码为例,可以计算出onlineChatBtn加载图片用了48ms,而它的父视图初始化用了305ms,这些都是在主线程执行的。

    4. 小结

    通过以上这些操作,我们能够看到两个标记之间具体执行了哪些代码,以及哪些代码比较耗时,进而可以有针对性地进行优化。

    相关文章

      网友评论

        本文标题:性能分析工具——os_signpost

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