• PerfView专题 (第一篇):如何寻找热点函数


    ## 一:背景

    准备开个系列来聊一下 PerfView 这款工具,熟悉我的朋友都知道我喜欢用 WinDbg,这东西虽然很牛,但也不是万能的,也有一些场景他解决不了或者很难解决,这时候借助一些其他的工具来辅助,是一个很不错的主意。

    很多朋友喜欢在项目中以记录日志的方式来监控项目的流转情况,其实 CoreCLR 也是这样的,参考如下代码:

    ``` C++

    void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
                                          BOOL record_ac_p)
    {
        dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
                     (size_t)acontext,
                     (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
    }

    void gc_heap::background_sweep()
    {
        //concurrent_print_time_delta ("finished with mark and start with sweep");
        concurrent_print_time_delta ("Sw");
        dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));

        //block concurrent allocation for large objects
        dprintf (3, ("lh state: planning"));
    }

    void gc_heap::background_ephemeral_sweep()
    {
        dprintf (3, ("bgc ephemeral sweep"));
    }

    ```

    那这些日志会送到哪里去呢,当然是 Windows 的 ETW 了,那有什么工具可以方便提取呢? PerfView 就是这么其中一款。

    这一篇我们做一个 CPU 爆高的场景下如何寻找 `热点函数` 的例子,看看如何用 PerfView 去挖。


    ## 二:PerfView 寻找热点函数

    很多场景下的 CPU 高,是因为某个或者某几个线程在高频的执行某个方法,有可能是死循环,有可能是陷入了CPU密集型方法内,解决这个问题一个好的思路就是对 CPU 进行采样,比如我的 12 核电脑。

    ``` C#

    0:000> !cpuid
    CP  F/M/S  Manufacturer     MHz
     0  6,5,2                  2592
     1  6,5,2                  2592
     2  6,5,2                  2592
     3  6,5,2                  2592
     4  6,5,2                  2592
     5  6,5,2                  2592
     6  6,5,2                  2592
     7  6,5,2                  2592
     8  6,5,2                  2592
     9  6,5,2                  2592
    10  6,5,2                  2592
    11  6,5,2                  2592

    ```

    ### 1. 如何采样

    采样的原理就是周期性的去看下当前的 CPU 核中运行的几个线程正在执行什么方法, 当采样到了几万个或者几十万个样本之后,就可以对这些采集到的方法进行分组排序来找到 topN,那些 TopN 的方法自然就是导致 CPU 爆高可能的诱因。

    windbg 有一个 `!running` 命令可以用来显示当前处理器中正在运行的线程。

    ``` C#

    lkd> !running

    System Processors:  (0000000000000fff)
      Idle Processors:  (000000000000065e)

           Prcbs             Current         (pri) Next            (pri) Idle
      0    fffff80268a33180  ffffaf8ec9bd8080 (15)                       fffff8026b526600  ................
      5    ffffd900e1700180  ffffaf8eca36b080 ( 8)                       ffffd900e170b340  ................
      7    ffffd900e1900180  ffffaf8ec2f18080 ( 8)                       ffffd900e190b340  ................
      8    ffffd900e1a00180  ffffd900e1a0b340 ( 0)                       ffffd900e1a0b340  ................
     11    ffffd900e1d00180  ffffaf8eb6bee080 ( 8)                       ffffd900e1d0b340  ................
     
    ```

    接下来写一个程序,让其中一个线程无限循环,然后通过 PerfView 去找这个热点。

    ``` C#

        internal class Program
        {
            static void Main(string[] args)
            {
                Task.Run(() => Test1());    //Test1 故意死循环
                Task.Run(() => Test2());    //Test2 是一个正常函数

                Console.WriteLine("我是主线程!");
                Console.ReadLine();
            }

            static void Test1()
            {
                var i = 10;
                var b = true;

                while (i > 0)
                {
                    b = !b;
                }
            }

            static void Test2()
            {
                for (int i = 0; i < 10000; i++)
                {
                    var j = string.Join(",", Enumerable.Range(0, 100));
                }

                Console.WriteLine("Test执行结束");
            }
        }

    ```

    ### 2. 使用 PerfView 采样

    点击菜单中的 `Collect -> Collect` ,弹出如下面板。

    ![](https://hxc-test.oss-cn-hangzhou.aliyuncs.com/PYZ/20220811092825.png)

    在这个面板中,选中如下几项。

    1)CPU Samples:

        设置对 CPU 进行采样。

    2)CPU Sample Interval Msec

        设置采样的频次是 1ms/次。

    3)Max Collect Sec

        设置总共采样多少秒,这里设置为 15 秒。

    4).NET Symbol Collection

        用来从微软符号服务器上拉取符号,和采样无关哈。


    上面都设置完毕后,就可以点击 `Start Collection` 采集了,不出意外的话,15s 之后你就会看到如下的截图。

    ![](https://hxc-test.oss-cn-hangzhou.aliyuncs.com/PYZ/20220811093254.png)

    接下来点击 `CPU Stacks`,在弹出的面板中选中我们的 `程序`,双击之后就可以打开如下面板。

    ![](https://hxc-test.oss-cn-hangzhou.aliyuncs.com/PYZ/20220811093819.png)

    从图中可以看到,当前采样了 `15622` 个样本,符合 `15 * 1000` ,接下来把上面的 `GroupPats` 默认分组给清掉,截图如下:

    ![](https://hxc-test.oss-cn-hangzhou.aliyuncs.com/PYZ/20220811100304.png)

    从图中可以看到当前 `Test1()` 方法在 15622 个样本中占比 `97.9%`,命中次数高达 15290 次,很明显这是一个绝对的 `热点函数`,接下来就是翻源码为什么 Test1 这么高频?

    如果你想看鸡肋的 `火焰图`,可以点击 `Flame Graph` 列表项。

    ![](https://hxc-test.oss-cn-hangzhou.aliyuncs.com/PYZ/20220811100412.png)

    好了,本篇就先聊这么多吧。

  • 相关阅读:
    【cmake实战八】cmake 常用变量
    Leetcode 481. 神奇字符串
    1785. 构成特定和需要添加的最少元素
    字节网上面经整理(100道面试题)二篇
    NTMFS4C05NT1G N-CH 30V 11.9A MOS管,PDF
    5.5 漏洞扫描:Web安全漏洞扫描及审计
    springboot连接rabbitmq报错:Failed to checkredeclare auto-delete queue(s)
    基于Java的校园“研帮”系统的设计与实现毕业设计源码201433
    计算机毕业设计ssm+vue基本微信小程序的一起考研学习系统 uniapp 小程序
    编译openMVG出现的错误的解决
  • 原文地址:https://blog.csdn.net/huangxinchen520/article/details/126498893