Application Insights Profiler for .NET 跟踪显示应用在处理请求时,哪些方法调用和代码行占用的时间最多。 假设你正在运行 Web 性能测试。 需要跟踪才能了解 Web 应用在负载下如何运行。
本文介绍如何生成发到服务的流量、查看.NET探查器跟踪,以及读取性能数据和调用堆栈来识别瓶颈。
在本文中,您可以:
- 通过启动 Web 性能测试或启动 Profiler 按需会话来为 Web 应用生成流量。
- 完成负载测试或 Profiler 会话后,查看 Profiler 跟踪。
- 读取 .NET 分析器的性能数据和调用堆栈。
为 Azure 服务生成流量
要使 .NET Profiler 能够上传跟踪数据,您的服务必须主动处理请求。
在 Azure 门户中,打开 Application Insights 资源。 在左侧菜单中,选择“ 调查>性能”。
在性能窗格中,从 Profiler 设置的顶部菜单中选择 Profiler。
加载 Profiler 设置页面后,选择“ 立即分析”。
查看 .NET 分析器跟踪数据
跟踪浏览器显示以下信息:
| 筛选器 | 说明 |
|---|---|
| 个人资料树 v. 火焰图 | 以树状或图形显示踪迹。 |
| 热路径 | 选择以打开最大的叶节点。 大多数情况下,此节点与性能瓶颈相邻。 |
| 框架依赖项 | 选择查看与跟踪关联的每个已跟踪的框架依赖项。 |
| 隐藏事件 | 输入要隐藏在跟踪视图中的字符串。 选择建议的事件以查看建议。 |
| 事件 | 事件或函数名称。 树中显示了代码与发生的事件(例如 SQL 和 HTTP 事件)的混合形式。 最前面的事件表示请求总持续时间。 |
| 模块 | 发生跟踪事件或函数的模块。 |
| 线程时间 | 从操作开始到操作结束之间的时间间隔。 |
| 时间线 | 函数或事件运行的时间相对于其他函数运行的时间。 |
读取.NET探查器性能数据和调用堆栈
.NET Profiler 结合使用采样方法和检测来分析应用程序的性能。 在执行详细收集的过程中,.NET Profiler:
每毫秒对每个计算机 CPU 的指令指针进行采样。
每个样本都捕获了线程的整个调用堆栈,其中提供了高层和底层抽象的详细信息。
收集事件以跟踪活动相关性和因果关系,包括:
- 上下文切换事件
- 任务并行库 (TPL) 事件
- 线程池事件
时间线视图显示来自采样和检测结果的调用堆栈。 每个示例捕获线程的完整调用堆栈。 它包括来自 Microsoft .NET Framework 和你引用的任何其他框架的代码。
对象分配 (clr!JIT_New or clr!JIT_Newarr1)
clr!JIT_New和clr!JIT_Newarr1是 .NET Framework 中从托管堆分配内存的辅助函数。
-
clr!JIT_New在分配对象时调用 。 -
clr!JIT_Newarr1在分配对象数组时调用 。
这两个函数通常可快速工作。 如果 clr!JIT_Newarr1 或 clr!JIT_New 在您的时间线中占用了较多时间,则代码可能分配了大量对象并消耗了大量内存。
加载代码 (clr!ThePreStub)
clr!ThePreStub是 .NET Framework 中的帮助程序函数,它准备代码以供初始执行,通常包括实时 (JIT) 编译。 对于每个 C# 方法,.NET框架运行时在一个进程中最多调用clr!ThePreStub一次。
如果 clr!ThePreStub 请求需要额外的时间,则这是执行该方法的第一个请求。 .NET Framework 运行时需要大量时间来加载第一个方法。 请注意以下几点:
- 使用在用户访问代码之前运行该部分代码的预热过程。
- 在程序集上运行本机映像生成器 (
ngen.exe)。
锁争用 (clr!JITutil_MonContention 或 clr!JITutil_MonEnterWorker)
clr!JITutil_MonContention 或 clr!JITutil_MonEnterWorker 指示当前线程正在等待释放锁。 您在以下情况下通常会看到此文本:
- 执行 C#
lock语句 - 调用
Monitor.Enter方法 - 使用
MethodImplOptions.Synchronized特性调用方法
如果线程 A 获取了某个锁,而线程 B 在线程 A 释放该锁之前尝试获取同一个锁,此时通常会发生锁争用。
加载代码 ([COLD])
如果.NET框架运行时首次运行未优化的代码,则方法名称包含[COLD]:
mscorlib.ni![COLD]System.Reflection.CustomAttribute.IsDefined
每个方法最多会在过程中出现一次。
如果请求加载代码需要相当长的时间,则表示该请求首次执行方法的未优化部分。 请考虑使用在用户访问代码之前运行该部分代码的预热过程。
发送 HTTP 请求
例如 HttpClient.Send ,指示代码正在等待 HTTP 请求完成。
数据库操作
例如 SqlCommand.Execute ,指示代码正在等待数据库操作完成。
等待 (AWAIT_TIME)
AWAIT_TIME 指示代码正在等待另一个任务完成。 此延迟与 C# await 语句一起发生。 当代码执行 C# await时:
- 线程展开并将控制权返回到线程池。
- 没有任何阻塞的线程会等待
await完成。
但是,从逻辑上讲,执行 await 操作的线程被阻止,等待操作完成。 该 AWAIT_TIME 语句表示阻塞时间,即等待任务完成所花费的时间。
如果 AWAIT_TIME 似乎出现在框架代码而非你的代码中,.NET Profiler 可能显示的是:
- 运行 的框架代码
- 记录关于
await的遥测数据的代码
若要仅显示代码并更轻松地查看来源位置 await ,请在页面顶部取消选择 框架依赖项。
阻塞时间
BLOCKED_TIME 指示代码正在等待另一个资源可用。 例如,它可能在等待:
- 同步对象
- 可用的线程
- 请求完成
非托管异步
为了使 .NET Profiler 能够跨线程跟踪异步调用,.NET Framework 会发出 Windows 事件跟踪(ETW)事件,并在线程之间传递活动 ID。 由于非托管(本机)代码和一些较旧的异步代码样式缺少这些事件和活动 ID,因此.NET Profiler 无法跟踪线程和线程上运行的函数。
.NET Profiler 将此项标记为调用堆栈中的非托管 Async。 下载 ETW 文件以使用 PerfView 获取更多见解。
CPU 时间
在跟踪中标记CPU_TIME的 CPU 时间是一个.NET Profiler 调用堆栈指示器,显示 CPU 正忙于执行采样线程的指令。 当调用堆栈中的某个节点上出现 CPU_TIME 时,.NET Profiler 会在线程正在处理器上主动运行代码而不是等待其他资源时对该线程进行采样。 较高的 CPU_TIME 值表明,相关方法或代码行中的工作受计算限制。
磁盘时间
磁盘时间是一个.NET Profiler 调用堆栈指示器(规范标签磁盘时间),显示应用程序正在执行磁盘操作,例如从存储读取或写入存储。 当磁盘时间出现在跟踪中的节点上时,采样线程正在等待或执行磁盘 I/O,而不是执行 CPU 指令。 较高的磁盘时间值表明,相关方法或代码行中存在 I/O 密集型工作。
网络时间
网络时间是一个.NET探查器调用堆栈指示器(规范标签网络时间),显示应用程序正在执行网络操作,例如通过连接发送和接收数据。 当网络时间出现在跟踪中的节点上时,采样线程正在等待或执行网络 I/O,而不是执行 CPU 指令。 较高的网络时间值表明相关方法或代码行中存在受网络限制的工作。
“时间”列
When 列显示某个节点随时间收集到的各种 包含式 样本。 .NET Profiler 将请求的整个时间范围划分为 32 个时间桶,节点的包含式样本会在这些时间桶中累加。 每个存储桶显示为条形图。 条形的高度表示缩放后的值。
对于以下节点,条表示存储桶中某个资源的消耗量:
- 标记为
CPU_TIME或BLOCKED_TIME的节点。 - 与使用资源(例如 CPU、磁盘或线程)有明显关系的节点。
对于这些指标,可以通过使用多个资源来获取大于 100% 的值。 例如,如果在某个时间间隔内平均使用两个 CPU,则将达到 200%。