如何使用 dotTrace 来诊断 netcore 应用的性能问题

最近在为 Newbe.Claptrap 做性能升级,因此将过程中使用到的 dotTrace 软件的基础用法介绍给各位开发者。

Newbe.Claptrap 是一个用于轻松应对并发问题的分布式开发框架。如果您是首次阅读本系列文章。建议可以先从本文末尾的入门文章开始了解。

开篇摘要

dotTrace 是 Jetbrains 公司为 .net 应用提供的一款 profile 软件。有助于对于软件中的耗时函数和内存问题进行诊断分析。

本篇,我们将使用 Jetbrains 公司的 dotTrace 软件对一些已知的性能问题进行分析。从而使读者能够掌握使用该软件的基本技能。

过程中我们将搭配一些经典的面试问题进行演示,逐步解释该软件的使用。

此次示例使用的是 Rider 作为主要演示的 IDE。开发者也可以使用 VS + Resharper 做出相同的效果。

如何获取 dotTrace

dotTrace 是付费软件。目前只要购买 dotUltimate 及以上的许可证便可以直接使用该软件。

当然,该软件也包含试用版本,可以免费开启 7 天的试用时间。Jetbrains 的 IDE 购买满一年以上即可获取一个当前最新的永久使用版本。

或者也可以直接购买 Jetbrains 全家桶许可证,一次性全部带走。

经典场景再现

接下来,我们通过一些经典的面试问题,来体验一下如何使用 dotTrace。

何时要使用 StringBuilder

这是多么经典的面试问题。能够看到这篇文章的朋友,我相信各位都知道 StringBuilder 能够减少 string 直接拼接的碎片,减少内存压力这个道理。

我们这是真的吗?会不会只是面试官想要刁难我,欺负我信息不对称呢?

没有关系,接下来,让我们使用 dotTrace 来具体的结合代码来分析一波。看看使用 StringBuilder 究竟有没有减低内存分配的压力。

首先,我们创建一个单元测试项目,并添加以下这样一个测试类:

using System.Linq;
using System.Text;
using NUnit.Framework;namespace Newbe.DotTrace.Tests
{public class X01StringBuilderTest{[Test]public void UsingString(){var source = Enumerable.Range(0, 10).Select(x => x.ToString()).ToArray();var re = string.Empty;for (int i = 0; i < 10_000; i++){re += source[i % 10];}}[Test]public void UsingStringBuilder(){var source = Enumerable.Range(0, 10).Select(x => x.ToString()).ToArray();var sb = new StringBuilder();for (var i = 0; i < 10_000; i++){sb.Append(source[i % 10]);}var _ = sb.ToString();}}
}

然后,如下图所示,我们将 Rider 中的 profile 模式设置为 Timeline 。

TimeLine 是多种模式中的一种,相较而言,该模式可以更全面的了解各个线程的工作情况,包括有内存分配、IO 处理、锁、反射等等多维度数据。这将会作为本示例主要使用的一种模式。

接着,如下图所示,通过单元测试左侧的小图标启动对应测试的 profile。

启动 profile 之后,等待一段时间之后,便会出现最新生成的 timeline 报告。查看报告的位置如下所示:

右键选择对应的报告,选择”Open in External Viewer”,便可以使用 dotTrace 打开生成好的报告。

那么首先,让我打开第一个报告,查看 UsingString 方法生成的报告。

如下图所示,选择 .Net Memory Allocations 以查看该测试运行过程中分配的内存数额。

根据上图我们可以得出以下结论:

  1. 在这测试中,有 102M 的内存被分配给 String 。注意,在 dotTrace 中显示的分配是指整个运行过程中全部分配的内存。即使后续被回收,该数值也不会减少。

  2. 内存的分配只要在 CLR Worker 线程进行。并且非常的密集。

Tip:Timeline 所显示的运行时间比正常运行测试的时间更长,因为在 profile 过程中需要对数据进行记录会有额外的消耗。

因此,我们就得出了第一个结论:使用 string 进行直接拼接,确实会消耗更多的内存分配。

接着,我们继续按照上面的步骤,查看一下 UsingStringBuilder 方法的报告,如下所示:

根据上图,我们可以得出第二个结论:使用 StringBuilder 可以明显的减少相较于 string 直接拼接所消耗的内存。

当然,我们得到的最终的结论其实是:看来面试官不是糊弄人。

class 和 struct 对内存有什么影响

class 和 struct 的区别有很多,面试题常客了。其中,两者在内存方面就存在区别。

那么我们通过一个测试来看看区别。

using System;
using System.Collections.Generic;
using NUnit.Framework;namespace Newbe.DotTrace.Tests
{public class X02ClassAndStruct{[Test]public void UsingClass(){Console.WriteLine($"memory in bytes before execution: {GC.GetGCMemoryInfo().TotalAvailableMemoryBytes}");const int count = 1_000_000;var list = new List<Student>(count);for (var i = 0; i < count; i++){list.Add(new Student{Level = int.MinValue});}list.Clear();var gcMemoryInfo = GC.GetGCMemoryInfo();Console.WriteLine($"heap size: {gcMemoryInfo.HeapSizeBytes}");Console.WriteLine($"memory in bytes end of execution: {gcMemoryInfo.TotalAvailableMemoryBytes}");}[Test]public void UsingStruct(){Console.WriteLine($"memory in bytes before execution: {GC.GetGCMemoryInfo().TotalAvailableMemoryBytes}");const int count = 1_000_000;var list = new List<Yueluo>(count);for (var i = 0; i < count; i++){list.Add(new Yueluo{Level = int.MinValue});}list.Clear();var gcMemoryInfo = GC.GetGCMemoryInfo();Console.WriteLine($"heap size: {gcMemoryInfo.HeapSizeBytes}");Console.WriteLine($"memory in bytes end of execution: {gcMemoryInfo.TotalAvailableMemoryBytes}");}public class Student{public int Level { get; set; }}public struct Yueluo{public int Level { get; set; }}}
}

代码要点:

  1. 两个测试,分别创建 1,000,000 个 class 和 struct 加入到 List 中。

  2. 运行测试之后,在测试的末尾输出当前堆空间的大小。

按照上一节提供的基础步骤,我们对比两个方法生成的报告。

UsingClass

UsingStruct

对比两个报告,可以得出以下这些结论:

  1. Timeline 报告中的内存分配,只包含分配在堆上的内存情况。

  2. struct 不需要分配在堆上,但是,数组是引用对象,需要分配在堆上。

  3. List 自增的过程本质是扩张数组的特性在报告中也得到了体现。

  4. 另外,没有展示在报告上,而展示在测试打印文本中可以看到,UsingStruct 运行之后的堆大小也证实了 struct 不会被分配在堆上。

装箱和拆箱

经典面试题 X3,来,上代码,上报告!

using NUnit.Framework;namespace Newbe.DotTrace.Tests
{public class X03Boxing{[Test]public void Boxing(){for (int i = 0; i < 1_000_000; i++){UseObject(i);}}[Test]public void NoBoxing(){for (int i = 0; i < 1_000_000; i++){UseInt(i);}}public static void UseInt(int age){// nothing}public static void UseObject(object obj){// nothing}}
}

Boxing, 发生装箱拆箱

NoBoxing,未发生装箱拆箱

对比两个报告,可以得出以下这些结论:

  1. 没有买卖就没有杀害,没有装拆就没有分配消耗。

Thread.Sleep 和 Task.Delay 有什么区别

经典面试题 X4,来,上代码,上报告!

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;namespace Newbe.DotTrace.Tests
{public class X04SleepTest{[Test]public Task TaskDelay(){return Task.Delay(TimeSpan.FromSeconds(3));}[Test]public Task ThreadSleep(){return Task.Run(() => { Thread.Sleep(TimeSpan.FromSeconds(3)); });}}
}

ThreadSleep

TaskDelay

对比两个报告,可以得出以下这些结论:

  1. 在 dotTrace 中 Thread.Sleep 会被单独标记,因为这是一种性能不不佳的做法,容易造成线程饥饿。

  2. Thread.Sleep 比起 Task.Delay 会多出一个线程处于 Sleep 状态

阻塞大量的 Task 真的会导致应用一动不动吗

有了上一步的结论,笔者产生了一个大胆的想法。我们都知道线程的有限的,那如果启动非常多的 Thread.Sleep 或者 Task.Delay 会如何呢?

来,代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;namespace Newbe.DotTrace.Tests
{public class X04SleepTest{[Test]public Task RunThreadSleep(){return Task.WhenAny(GetTasks(50));IEnumerable<Task> GetTasks(int count){for (int i = 0; i < count; i++){var i1 = i;yield return Task.Run(() =>{Console.WriteLine($"Task {i1}");Thread.Sleep(int.MaxValue);});}yield return Task.Run(() => { Console.WriteLine("yueluo is the only one dalao"); });}}[Test]public Task RunTaskDelay(){return Task.WhenAny(GetTasks(50));IEnumerable<Task> GetTasks(int count){for (int i = 0; i < count; i++){var i1 = i;yield return Task.Run(() =>{Console.WriteLine($"Task {i1}");return Task.Delay(TimeSpan.FromSeconds(int.MaxValue));});}yield return Task.Run(() => { Console.WriteLine("yueluo is the only one dalao"); });}}}
}

这里就不贴报告了,读者可以试一下这个测试,也可以将报告的内容写在本文的评论中参与讨论~

反射调用和表达式树编译调用

有时,我们需要动态调用一个方法。最广为人知的方式就是使用反射。

但是,这也是广为人知的耗时相对较高的方式。

这里,笔者提供一种使用表达式树创建委托来取代反射提高效率的思路。

那么,究竟有没有减少时间消耗呢?好报告,自己会说话。

using System;
using System.Diagnostics;
using System.Linq.Expressions;
using NUnit.Framework;namespace Newbe.DotTrace.Tests
{public class X05ReflectionTest{[Test]public void RunReflection(){var methodInfo = GetType().GetMethod(nameof(MoYue));Debug.Assert(methodInfo != null, nameof(methodInfo) + " != null");for (int i = 0; i < 1_000_000; i++){methodInfo.Invoke(null, null);}Console.WriteLine(_count);}[Test]public void RunExpression(){var methodInfo = GetType().GetMethod(nameof(MoYue));Debug.Assert(methodInfo != null, nameof(methodInfo) + " != null");var methodCallExpression = Expression.Call(methodInfo);var lambdaExpression = Expression.Lambda<Action>(methodCallExpression);var func = lambdaExpression.Compile();for (int i = 0; i < 1_000_000; i++){func.Invoke();}Console.WriteLine(_count);}private static int _count = 0;public static void MoYue(){_count++;}}
}

RunReflection,直接使用反射调用。

RunExpression,使用表达式树编译一个委托。

本篇小结

使用 dotTrace 可以查看方法的内存和时间消耗。本篇所演示的内容只是其中很小的部分。开发者们可以尝试上手,大有裨益。

本篇内容中的示例代码,均可以在以下链接仓库中找到:

  • https://github.com/newbe36524/Newbe.Demo

  • https://gitee.com/yks/Newbe.Demo

最后但是最重要!

如果读者对该内容感兴趣,欢迎转发、评论、收藏文章以及项目。

最近作者正在构建以反应式Actor模式事件溯源为理论基础的一套服务端开发框架。希望为开发者提供能够便于开发出 “分布式”、“可水平扩展”、“可测试性高” 的应用系统 ——Newbe.Claptrap

本篇文章是该框架的一篇技术选文,属于技术构成的一部分。

联系方式:

  • Github Issue

  • Gitee Issue

  • 公开邮箱 newbe-claptrap@googlegroups.com (发送到该邮箱的内容将被公开)

  • Gitter

  • QQ 群 610394020

您还可以查阅本系列的其他选文:

理论入门篇

  1. Newbe.Claptrap - 一套以 “事件溯源” 和 “Actor 模式” 作为基本理论的服务端开发框架

术语介绍篇

  1. Actor 模式

  2. 事件溯源(Event Sourcing)

  3. Claptrap

  4. Minion

  5. 事件 (Event)

  6. 状态 (State)

  7. 状态快照 (State Snapshot)

  8. Claptrap 设计图 (Claptrap Design)

  9. Claptrap 工厂 (Claptrap Factory)

  10. Claptrap Identity

  11. Claptrap Box

  12. Claptrap 生命周期(Claptrap Lifetime Scope)

  13. 序列化(Serialization)

实现入门篇

  1. Newbe.Claptrap 框架入门,第一步 —— 创建项目,实现简易购物车

  2. Newbe.Claptrap 框架入门,第二步 —— 简单业务,清空购物车

  3. Newbe.Claptrap 框架入门,第三步 —— 定义 Claptrap,管理商品库存

  4. Newbe.Claptrap 框架入门,第四步 —— 利用 Minion,商品下单

样例实践篇

  1. 构建一个简易的火车票售票系统,Newbe.Claptrap 框架用例,第一步 —— 业务分析

  2. 在线体验火车票售票系统

其他番外篇

  1. 谈反应式编程在服务端中的应用,数据库操作优化,从 20 秒到 0.5 秒

  2. 谈反应式编程在服务端中的应用,数据库操作优化,提速 Upsert

  3. 十万同时在线用户,需要多少内存?——Newbe.Claptrap 框架水平扩展实验

  4. docker-mcr 助您全速下载 dotnet 镜像

  5. 十多位全球技术专家,为你献上近十个小时的.Net 微服务介绍

  6. 年轻的樵夫哟,你掉的是这个免费 8 核 4G 公网服务器,还是这个随时可用的 Docker 实验平台?

  7. 如何使用 dotTrace 来诊断 netcore 应用的性能问题

GitHub 项目地址:https://github.com/newbe36524/Newbe.Claptrap

Gitee 项目地址:https://gitee.com/yks/Newbe.Claptrap

您当前查看的是先行发布于 www.newbe.pro 上的博客文章,实际开发文档随版本而迭代。若要查看最新的开发文档,需要移步 claptrap.newbe.pro。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307418.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode232. 用栈实现队列

一:题目 二:上码 class MyQueue { public:/**思路:1.设置两个栈,一个In,一个out,2.模拟入队的话 其实就是 往栈中添加元素3.模拟出队的话&#xff0c;那么是先进先出的 ,我们需要将in中的元素 导入到 out 中&#xff0c;然后出队的话其实也就是 out栈中的栈顶元素。*/stack<…

用.NetCore 编译国产老牌PHP论坛DiscuzX ,世界上最好的语言从此属于.Net 的一员

DiscuzX是北京康盛新创科技有限责任公司推出的一套通用的社区论坛软件系统。自2001年6月面世以来&#xff0c;Discuz!已拥有15年以上的应用历史和200多万网站用户案例&#xff0c;是全球成熟度最高、覆盖率最大的论坛软件系统之一。目前最新版本Discuz! X3.4正式版于2017年8月2…

leetcode225. 用队列实现栈

一:题目 二:上码 class MyStack { private:/**思路:1.我们每次push的时候 先push进队列,然后的话,将该元素前面的元素都出队重新push进该队列的尾部*/queue<int>q; public:/** Initialize your data structure here. */MyStack() {}/** Push element x onto stack. */voi…

使用 .NET 进行游戏开发

微软是一家综合性的网络公司&#xff0c;相信这点来说不用过多的赘述&#xff0c;没有人不知道微软这个公司&#xff0c;这些年因为游戏市场的回报&#xff0c;微软收购了很多的游戏公司还有独立工作室&#xff0c;MC我的世界就是最成功的的案例&#xff0c;现在市值是排在全世…

leetcode20. 有效的括号

一:题目 二:上码 class Solution { public:/**思路:- 这个题厉害之处在于其遍历到符号的左半部分的时候,我们是将对应的右半部分入队的- 那么如果存在对应的元素的话,那么我们就可以判断跟栈顶元素是否相等。*/bool isValid(string s) {stack<char> st;for (int i 0; i …

为什么我们总是「习惯性辩解」?

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「162」篇原创敬上大家好&#xff0c;我是Z哥。今天我们来讨论一个日常工作中很常见的现象&#xff1a;面对质疑&#xff0c;我们的第一反应是不承认&#xff0c;甚…

leetcode1047. 删除字符串中的所有相邻重复项

一:题目 二:上码 class Solution { public:string removeDuplicates(string s) {stack<char>st;string str;st.push(s[0]);for (int i 1; i < s.size(); i) {if (!st.empty() && s[i] st.top()) {//此时s[i]也没有入栈st.pop();} else {st.push(s[i]);}}whi…

如何理解Eating这个词?云原生与微服务专场介绍

点击上方“开源社”关注我们| 作者&#xff1a;开源社| 编辑&#xff1a;李明康| 责编&#xff1a;袁睿斌有一幅曾经传播很广的漫画&#xff0c;大意是&#xff1a;软件吞噬世界&#xff0c;开源吞噬软件。后来这个漫画又被人加了两条鱼&#xff1a;云计算吞噬开源&#xff0c;…

leetcode239. 滑动窗口最大值(java详解)

一:题目 二:思路 1:lc通过版 class Solution {/*思路:1.这里是要求出每一个窗口中的最大值,那么我们自然的想到如何用一个队列 可以使其每次队首出现最大值&#xff0c;那么我自然回想到大顶堆,但是用了之后&#xff0c;我们窗口中的元素位置就发生了变化那么当我们移动窗口的…

程序员修神之路--它可能是分布式系统中最重要的枢纽

“灵魂拷问分布式系统为什么需要注册中心呢&#xff1f;分布式系统注册中心有哪些坑&#xff1f;分布式系统注册中心怎么来实现呢&#xff1f;注册中心利用现成的组件很好实现吗&#xff1f;看到标题你可能会鄙视一下&#xff0c;注册中心有是什么讲的。注册中心作为现在架构中…

用Java刷算法题的常用数据结构(C++转Java)

文章目录一:前言1:为何刷题从C转java2:如何上手呢&#xff1f;二:输入1:常规的输入2:关于其他输入符在nextLine()之前用吃掉回车符的问题解决3:常见输入之我们输入一串数到容器中三:常用的数据结构1:数组2.List3:Map4:Set5.栈6:队列一:前言 1:为何刷题从C转java 平时除了写项…

Magicodes.IE 2.4发布

今天我们发布了2.4版本&#xff0c;这离不开大家对Magicodes.IE的支持&#xff0c;我们也对大家的意见以及需求不断的进行更新迭代&#xff0c;目前我们的发布频率平均在一周一个beta版本&#xff0c;一个月一个正式版本的更新&#xff0c;我们欢迎更多的开发者加入进来&#x…

7-1 简单词法分析

一:题目 二:思路 思路: 1.记得看书;不要一上来就莽;不然莽不过去的 2.这里我从书中了解到 f(0,b) 0; f(0,a) 1;f(1,c)1;f(1,b)3… 那么的话我们只要最终推导出f(1,b)3;那么的话就是一个满足要求的字符串; 注意我们入口部分一定是从 0 开始; 3.接下来就是要判断一些细枝末节 …

Java 生态碎片化 和 .NET生态的一致性

.NET Core是以MIT协议开源&#xff0c; Java是GPL协议开源。Java 8 SDK升级Oracle要收费这件事对于很多小公司是有着重大的影响的&#xff0c;Java生态越发碎片化&#xff0c;有众多的OpenJDK发行版&#xff0c;腾讯云和阿里都有OpenJDK发行版&#xff0c;龙芯也有MIPS版本的Op…

Power Automate Desktop概览

点击蓝字关注我们Microsoft Power Automate使得通过自动化重复性、耗时的任务来提高您的业务效率成为可能。Power Automate提供了一种更好的方法&#xff0c;通过数字和机器人过程自动化(RPA)在整个组织中完成任务。Microsoft Ignite 在线活动小伙伴们都有参加么&#xff1f;重…

超600人!近5小时直播!录屏+彩蛋+PPT…你要的都在这!

2020年9月26日下午&#xff0c;《NCF框架揭秘》直播交流会圆满落幕&#xff01;由盛派首席架构师苏震巍老师主持、分享&#xff0c;更有各路大咖&#xff0c;在线助力&#xff0c;干货满满&#xff01;点击视频 ☝ 回顾直播现场友情提示&#xff1a;如果公众号内视频无法显示高…

leetcode226. 翻转二叉树(Java)

一&#xff1a;题目 二:上码(前序解法) /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left,…

Chrome正在启用HTTP/3,支持IETF QUIC

Chromium 官方宣布 Chrome 正在部署到 HTTP/3 与 IETF QUIC。QUIC&#xff08;Quick UDP Internet Connections&#xff09;是 Google 推出的一个项目&#xff0c;旨在降低基于 TCP 通讯的 Web 延迟。QUIC 非常类似 TCPTLSSPDY &#xff0c;但是基于 UDP 实现的。它是 HTTP/3 的…

leetcode101. 对称二叉树

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

诊断日志知多少 | DiagnosticSource 在.NET上的应用

1. 引言最近为了解决ABP集成CAP时无法通过拦截器启用工作单元的问题&#xff0c;从小伙伴那里学了一招。借助DiagnossticSource&#xff0c;可以最小改动完成需求。关于DiagnosticSource晓东大佬18年在文章 在 .NET Core 中使用 Diagnostics (Diagnostic Source) 记录跟踪信息就…