DotTrace系列:2. 理解四大经典的诊断类型(下)

一:背景

1. 讲故事

前面我们已经聊过四大诊断类型中的前三个 Sampling,Tracine,Line-by-Line,这篇补上最后一个诊断类型 Timeline,这也是真实场景中使用最多的,它能够采集到所有它能采集到的,比如:

  • 线程栈数据 (函数执行时间)
  • ETW事件 (Windows日志)
  • TPL数据(方便绘制异步栈)
  • 按时序绘制时间轴 (Timeline)

二:Timeline 解读

1. 一个简单的测试案例

为了方便演示,我们还是用上一篇的矩阵运算的例子,参考代码如下:


using System;
using System.Diagnostics;

namespace MatrixOperations
{
    internal class Program
    {
        static void Main(string[] args)
        {
            const int baseSize = 1000;
            const int iterations = 3;

            for (int i = 0; i < iterations; i++)
            {
                int matrixSize = baseSize - (i * 100);
                PerformMatrixMultiplication(matrixSize);
            }
        }

        static void PerformMatrixMultiplication(int matrixSize)
        {
            Console.WriteLine($"\n=== 处理 {matrixSize}x{matrixSize} 矩阵 ===");

            Console.WriteLine("创建随机矩阵...");
            var matrixA = GenerateRandomMatrix(matrixSize, matrixSize);
            var matrixB = GenerateRandomMatrix(matrixSize, matrixSize);

            Console.WriteLine("执行矩阵乘法...");
            var timer = Stopwatch.StartNew();

            var resultMatrix = MultiplyMatrices(matrixA, matrixB);

            timer.Stop();
            Console.WriteLine($"运算完成,耗时: {timer.Elapsed.TotalSeconds:0.000} 秒");

            DisplayMatrixPreview(resultMatrix);
        }

        static double[,] GenerateRandomMatrix(int rows, int cols)
        {
            var random = new Random();
            var matrix = new double[rows, cols];

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    matrix[i, j] = random.NextDouble() * 100;
                }
            }

            return matrix;
        }

        static double[,] MultiplyMatrices(double[,] matrixA, double[,] matrixB)
        {
            int aRows = matrixA.GetLength(0);
            int aCols = matrixA.GetLength(1);
            int bCols = matrixB.GetLength(1);

            if (matrixA.GetLength(1) != matrixB.GetLength(0))
                throw new ArgumentException("矩阵维度不匹配");

            var result = new double[aRows, bCols];

            for (int i = 0; i < aRows; i++)
            {
                for (int j = 0; j < bCols; j++)
                {
                    double sum = 0;
                    for (int k = 0; k < aCols; k++)
                    {
                        sum += matrixA[i, k] * matrixB[k, j];
                    }
                    result[i, j] = sum;
                }
            }

            return result;
        }

        static void DisplayMatrixPreview(double[,] matrix, int previewSize = 3)
        {
            Console.WriteLine($"\n矩阵预览 (前{previewSize}x{previewSize}个元素):");

            int rows = Math.Min(previewSize, matrix.GetLength(0));
            int cols = Math.Min(previewSize, matrix.GetLength(1));

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    Console.Write($"{matrix[i, j],8:0.00} ");
                }
                Console.WriteLine();
            }
        }
    }
}

接下来打开 dotrace,选择 Timeline 模式,采样频次默认是 1000samples/s,即每秒1000次采样,这个相比 Sampling 模式的5~11s 要快得多,也让采集结果成倍的增加,如果你想采集的更密集些,可以设置为 8000 samples/sec,最后就是启动 Start,截图如下:

采集完成之后,就能看到如下的 采集结果界面,映入眼帘的就是🐂👃的Timeline 时间轴,

在卦中的时间轴上标记着二类数据:

  • 线程活动的时序分布。
  • GC Wait时间(GC触发时的累计阻塞时间)

结果都有了,接下来回答三个问题来熟悉下 Timeline 模式吧。

三:几个常见的疑问解答

1. 哪个函数最耗时

宏观观察时间轴,我们发现 Main线程的轴上有一段很长的深绿色,说明它曾在这个时段活动,接下来在 Thread State 面板中选择 Running 状态,然后选择 Main 线程进行过滤。

从卦中可以看到,在追踪的13s周期中,Main在第4s时开始发飙,从 Hotspots 下的 8s MultiplyMatrices 来看,时间都被它吃了,在 Running:CPU Core 中能看到线程大多都在 core11core9 上跑,找到可疑函数后,可以右键选择 Show Code 观察 MultiplyMatrices 方法的源代码,截图如下:

至此我们找到了耗费cpu的热点函数。

2. 为什么深绿色是不间断的

这是一个挺有意思的问题,熟悉操作系统知识的朋友应该知道Windows是抢占式操作系统,每个线程都会分配到一个时间片,在落地上用 量程(Quantum) 表示,所以这些间断的很显然是Main线程失宠得宠的一个过程,即图中的 粉色区域

接下来在 Thread State 面板中选择 Waiting,下钻 粉色条段

从卦中可以看到这个状态叫 Waiting for CPU,即正等待CPU再次调度。

3. 观察 GC 的运作情况

GC运作的详细信息其实用 perfview 是比较合适的,毕竟一个程序在运行过程中会很容易达到成千上万次GC,如果GC比较少的话,还是可以用 dottrace 观察一下的,接下来简单修改代码,在 MultiplyMatrices 方法的最后加上 GC.Collect(),参考如下:


        static double[,] MultiplyMatrices(double[,] matrixA, double[,] matrixB)
        {
            int aRows = matrixA.GetLength(0);
            int aCols = matrixA.GetLength(1);
            int bCols = matrixB.GetLength(1);

            if (matrixA.GetLength(1) != matrixB.GetLength(0))
                throw new ArgumentException("矩阵维度不匹配");

            var result = new double[aRows, bCols];

            for (int i = 0; i < aRows; i++)
            {
                for (int j = 0; j < bCols; j++)
                {
                    double sum = 0;
                    for (int k = 0; k < aCols; k++)
                    {
                        sum += matrixA[i, k] * matrixB[k, j];
                    }
                    result[i, j] = sum;
                }
            }

            GC.Collect();  //故意触发阻塞GC

            return result;
        }

启动 dottrace 跟踪,完成之后截图如下:

打开卦之后,选中 Main线程,GarbageCollection事件以及Running状态,可以清晰的看到,当前触发了 3次 阻塞GC,1次后台GC。

四:总结

整体上来说,dottrace最大的优点就是时间轴,在某些场景下比 perfview 的表格展示法更加清楚,timeline模式也是在真实场景中用的最多的一种洞察方式。

作为JetBrains社区内容合作者,如有购买jetbrains的产品,可以用我的折扣码 HUANGXINCHENG,有25%的内部优惠哦。

图片名称 原创作者: huangxincheng 转载于: https://www.cnblogs.com/huangxincheng/p/18942584
SQL+数据透视+VBA 使数据透视走向更灵活,更智能,更适用。 这个是我和师傅一撇首度合作,他提供了文件并提出了要求,我帮他实现其效果 下面从几个方面解释一下: 1、功能 一个源文件和一个通过用SQL查询生成的数据透视 将源文件拖到电脑的任意位置,甚至将文件名也改掉,用VBA配上代码和窗体找到文件,数据透视仍然能够正常工作 2、套用 现在来讲讲怎么使做出来的东东适应大家的需要 2、1 用OLE DB窗口引用工作或写SQL语句,因为用这个方法同VBA相通,copy下来代码区的的语句 2、2 打开透视文件,将透视中的字段全部拖出来,也就是变成一个空数据透视。 右击下面工作图标 或者 工具》宏》visual basic 编辑器,点击模块看到代码区 2、3 将2、1步骤copy的语句commandtext的数据Array中的引号中 .CommandText = Array(" ") 可能不同版本会有一些差别,同时SQL语句中如果添加了文本生成新字段,双引号要成对翻倍 如:"出库" AS 单选项 要改成 ""出库"" AS 单选项 2、4 语句太长的处理:在代码区如果你想好看一些,你可以插入“ _”来换行,当然不能插在一个单词或自动名等中间。 2、5 将文件存盘,重新打开就会有了数据,你可以将字段拖入数据透视中,创建你自己的数据透视, 2、6 这样文件就可以使用,相信VBA的引导不用教就可以交给别人使用了 下面附上代码,包含3个区: 1、 工作簿去,打开文件时工作 Private Sub Workbook_Open() Dim OP If Dir(Sheets("path").Range("A1")) = "" Then OP = MsgBox("源文件已被移走,请选择下列选项" + Chr(10) + "1、选择是,重新输入文件全名" + Chr(10) + "2、选择否,打开原有的数据透视" + Chr(10) + "3、选择取消,关闭文件", vbYesNoCancel, "Scarlett温馨提示") If OP = vbYes Then UserForm1.Show End If If OP = vbNo Then ActiveWorkbook.Close True End If If OP = vbCancel Then Exit Sub End If Else Call refreshpv End If End Sub 2、窗体区,实现文件的查找 Private Sub CommandButton1_Click() Dim fopen As FileDialog Set fopen = Application.FileDialog(msoFileDialogFilePicker) fopen.Show TextBox1.Value = fopen.SelectedItems(1) Set fopen = Nothing End Sub Private Sub CommandButton2_Click() If InStr(TextBox1.Value, ".") > 0 Then Sheets("path").Range("A1") = TextBox1.Value Call refreshpv unload me Else MsgBox "文件名要带路径含后缀的文件名", "Scarlett_88温馨提示" TextBox1.SetFocus End If End Sub Private Sub CommandButton3_Click() Unload Me End Sub Private Sub TextBox1_Change() End Sub Private Sub UserForm_Activate() End Sub Private Sub UserForm_Click() TextBox1.Value = Sheets("path").Range("A1") End Sub 3、模块区,实现SQL语句的地址更新和刷新数据透视的数据源 Sub refreshpv() With ActiveSheet.PivotTables("数据透视1").PivotCache .Connection = Array( _ "OLEDB;Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Sourc
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值