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 中能看到线程大多都在 core11 和 core9 上跑,找到可疑函数后,可以右键选择 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%的内部优惠哦。

DotTrace系列:2. 理解四大经典的诊断类型(下)的更多相关文章
- 老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(三)
老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(三) 前言 上一篇文章老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(二)从三个问题导入,分析了Spring ...
- OpenStack实践系列⑦深入理解neutron和虚拟机
OpenStack实践系列⑦深入理解neutron和虚拟机 五.深入理解Neutron 5.1 虚拟机网卡和网桥 [root@node1 ~]# ifconfig brq65c11cc3-8e: fl ...
- 微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件
原文:微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件 开篇介绍 大多数情况下我们的 SSIS 包都会配置在 SQL Agent ...
- 【Xamarin挖墙脚系列:时刻下载最新的Mac环境下的Xamarin安装包】
原文:[Xamarin挖墙脚系列:时刻下载最新的Mac环境下的Xamarin安装包] 打开这两个地址,就能看到最新的安装包了.... http://www.jianshu.com/p/c67c14b3 ...
- C++ : 从栈和堆来理解C#中的值类型和引用类型
C++中并没有值类型和引用类型之说,标准变量或者自定义对象的存取默认是没有区别的.但如果深入地来看,就要了解C++中,管理数据的两大内存区域:栈和堆. 栈(stack)是类似于一个先进后出的抽屉.它的 ...
- JavaScript进阶系列04,函数参数个数不确定情况下的解决方案
本篇主要体验函数参数个数不确定情况下的一个解决方案.先来看一段使用函数作为参数进行计算的实例. var calculate = function(x, y, fn) { return fn(x, y) ...
- 【ABAP系列】SAP ABAP ALV里日期类型的F4帮助
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP ALV里日期类 ...
- SpringCloud 源码系列(5)—— 负载均衡 Ribbon(下)
SpringCloud 源码系列(4)-- 负载均衡 Ribbon(上) SpringCloud 源码系列(5)-- 负载均衡 Ribbon(下) 五.Ribbon 核心接口 前面已经了解到 Ribb ...
- vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(下)
其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(下) 上篇 我们已经成功引入 element-ui.axios.mock.iconfont.nprogress,本篇继续 ...
- 前端提升生产力系列三(vant3 vue3 移动端H5下拉刷新,上拉加载组件的封装)
| 在日常的移动端开发中,经常会遇到列表的展示,以及数据量变多的情况下还会有上拉和下拉的操作.进入新公司后发现移动端好多列表,但是在看代码的时候发现,每个列表都是单独的代码,没有任何的封装,都是通过v ...
随机推荐
- CAS和OAuth2.0区别
CAS和OAuth2.0区别 CAS (Central Authentication Service) 和 OAuth 2.0 都是身份验证和授权技术,但它们在工作原理和适用场景上有明显的差异. CA ...
- Effective Java理解笔记系列-第1条-何时考虑用静态工厂方法替代构造器?
为什么写这系列博客? 在阅读<Effective Java>这本书时,我发现有许多地方需要仔细认真地慢慢阅读并且在必要时查阅相关资料才能彻底搞懂,相信有些读者在阅读此书时也有类似感受:同时 ...
- 【SpringCloud】Zookeeper服务注册与发现
Zookeeper服务注册与发现 Eureka停止更新了,你怎么办 https://github.com/Netflix/eureka/wiki SpringCloud整合Zookeeper替代Eur ...
- PII is hidden.
使用 Microsoft.AspNetCore.Authentication.JwtBearer 做验证的时候报错如下: IDX10503: Signature validation failed. ...
- 调用 restful的api的方法
var // myurl : string; tmpstr : String;// string; RespData :TStringStream; sendData : TStringList; j ...
- 轻松的工作(deepseek)
组长:"这里有一百多个地震波形文件,把每一个地震建立一个文件夹,并把地震波形放到对应日期的地震中." 我想:一个一个整好麻烦想摸会鱼 让我们来deepseek吧~ 首先,生成文件夹 ...
- rabbitmq的消息的有顺序性
一.rabbitmq:拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点:或者就一个queue但是对应一个consumer,然后这个consumer内部用内 ...
- ESP-IDF教程1 开发环境
1.开发环境 对于 ESP32 系列芯片的开发环境主要有如下几种方式: ESP-IDF(Espressif IoT Development Framework) Arduino PlatformIO ...
- MySQL 中 varchar 和 char 有什么区别?
MySQL 中 varchar 和 char 的区别 在 MySQL 中,VARCHAR 和 CHAR 是两种常用的字符串类型,它们在存储方式.长度限制和使用场景等方面存在显著区别. 1. 定义与存储 ...
- MySQL 索引的最左前缀匹配原则是什么?
MySQL 索引的最左前缀匹配原则 最左前缀匹配原则是 MySQL 使用联合索引时的一个重要优化规则.它指的是在查询条件中,只有符合索引最左侧字段开始的连续前缀部分时,索引才能被有效利用. 1. 最左 ...