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


    一:背景

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

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

    1. void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
    2.                                       BOOL record_ac_p)
    3. {
    4.     dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
    5.                  (size_t)acontext,
    6.                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
    7. }
    8. void gc_heap::background_sweep()
    9. {
    10.     //concurrent_print_time_delta ("finished with mark and start with sweep");
    11.     concurrent_print_time_delta ("Sw");
    12.     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
    13.     //block concurrent allocation for large objects
    14.     dprintf (3, ("lh state: planning"));
    15. }
    16. void gc_heap::background_ephemeral_sweep()
    17. {
    18.     dprintf (3, ("bgc ephemeral sweep"));
    19. }

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

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

    二:PerfView 寻找热点函数

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

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

    1. 如何采样

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

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

    1. lkd> !running
    2. System Processors:  (0000000000000fff)
    3.   Idle Processors:  (000000000000065e)
    4.        Prcbs             Current         (pri) Next            (pri) Idle
    5.   0    fffff80268a33180  ffffaf8ec9bd8080 (15)                       fffff8026b526600  ................
    6.   5    ffffd900e1700180  ffffaf8eca36b080 8)                       ffffd900e170b340  ................
    7.   7    ffffd900e1900180  ffffaf8ec2f18080 8)                       ffffd900e190b340  ................
    8.   8    ffffd900e1a00180  ffffd900e1a0b340 0)                       ffffd900e1a0b340  ................
    9.  11    ffffd900e1d00180  ffffaf8eb6bee080 8)                       ffffd900e1d0b340  ................
    10.  

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

    1.     internal class Program
    2.     {
    3.         static void Main(string[] args)
    4.         {
    5.             Task.Run(() => Test1());    //Test1 故意死循环
    6.             Task.Run(() => Test2());    //Test2 是一个正常函数
    7.             Console.WriteLine("我是主线程!");
    8.             Console.ReadLine();
    9.         }
    10.         static void Test1()
    11.         {
    12.             var i = 10;
    13.             var b = true;
    14.             while (i > 0)
    15.             {
    16.                 b = !b;
    17.             }
    18.         }
    19.         static void Test2()
    20.         {
    21.             for (int i = 0; i < 10000; i++)
    22.             {
    23.                 var j = string.Join(",", Enumerable.Range(0100));
    24.             }
    25.             Console.WriteLine("Test执行结束");
    26.         }
    27.     }

    2. 使用 PerfView 采样

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

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

    1)CPU Samples:

    设置对 CPU 进行采样。

    2)CPU Sample Interval Msec

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

    3)Max Collect Sec

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

    4).NET Symbol Collection

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

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

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

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

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

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

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

  • 相关阅读:
    工业控制系统安全标准
    人人商城app禁用
    Django框架web开发实战:Model和ORM学习(三)
    win10部署 Mistral-7B 文本生成模型
    视频服务HDR Vivid 还原色彩,让所见成“真”
    Java面试题
    Agent-FLAN 技术报告——社区翻译版
    Android kotlin系列讲解(入门篇)使用HTTP访问网络
    PO模式在selenium自动化测试框架有什么好处
    06:HAL----定时器
  • 原文地址:https://blog.csdn.net/biyusr/article/details/126303746