.NET性能系列文章一:.NET7的性能改进

这些方法在.NET7 中变得更快

80fc1b7c6fe7599f68ae0f56123a1b9e.jpeg

照片来自 CHUTTERSNAP[1] 的 Unsplash[2]

欢迎阅读.NET 性能系列的第一章。这一系列的特点是对.NET 世界中许多不同的主题进行研究、比较性能。正如标题所说的那样,本章节在于.NET7 中的性能改进。你将看到哪种方法是实现特定功能最快的方法,以及大量的技巧和敲门,如何付出较小的代价就能最大化你代码性能。如果你对这些主题感兴趣,那请您继续关注。

.NET 7 目前(17.10.2022)处于预览阶段,将于 2022 年 11 月发布。通过这个新版本,微软提供了一些大的性能改进。这篇 .NET 性能系列的第一篇文章,是关于从.NET6 到.NET7 最值得注意的性能改进。

LINQ

最相关的改进肯定是在 LINQ 中,在.NET 7 中dotnet 社区[3]利用 LINQ 中对数字数组的处理来使用Vector<T>(SIMD)。这大大改善了一些 LINQ 方法性能,你可以在List<int>int[]以及其他数字集合上调用。现在 LINQ 方法也能直接访问底层数组,而不是使用枚举器访问。让我们来看看这些方法相对于.NET 6 是如何表现的。

我使用BenchmarkDotNet[4]来比较.NET6 和.NET7 相同代码的性能。

1. Min 和 Max 方法

首先是 LINQ 方法Min()Max()。它们被用来识别数字枚举中的最低值或最高值。新的实现特别要求有一个先前枚举的集合作为源,因此我们必须在这个基准测试中创建一个数组。

[Params(1000)]
public int Length { get; set; }private int[] arr;[GlobalSetup]
public void GlobalSetup() => arr = Enumerable.Range(0, Length).ToArray();[Benchmark]
public int Min() => arr.Min();[Benchmark]
public int Max() => arr.Max();

在.NET 6 和.NET 7 上执行这些基准,在我的机器上会得出以下结果。

方法运行时数组长度平均值比率分配
Minoutside_default.png10003,494.08 ns53.2432 B
Minoutside_default.png100065.64 ns1.00-






Maxoutside_default.png10003,025.41 ns45.9232 B
Maxoutside_default.png100065.93 ns1.00-
223acd6a37d0ee8c5da61482716b87a1.png

这里非常突出的是新的.NET7 所展示的性能改进有多大。我们可以看到与.NET 6 相比,改进幅度超过 4500%。这不仅是因为在内部实现中使用了另一种类型,而且还因为不再发生额外的堆内存分配。

2. Average 和 Sum

另一个很大的改进是Average()Sum()方法。当处理大的double集合时,这些性能优化能展现出更好的结果,这就是为什么我们要用一个double[]来测试它们。

[Params(1000)]
public int Length { get; set; }private double[] arr;[GlobalSetup]
public void GlobalSetup()
{var random = new Random();arr = Enumerable.Range(0, Length).Select(_ => random.NextDouble()).ToArray();
}[Benchmark]
public double Average() => arr.Average();[Benchmark]
public double Sum() => arr.Sum();

结果显示,性能显著提高了 500%以上,而且同样没有了内存分配!

方法运行时数组长度平均值比率分配
Averageoutside_default.png10003,438.0 ns5.5032 B
Averageoutside_default.png1000630.3 ns1.00-






Sumoutside_default.png10003,303.8 ns5.2532 B
Sumoutside_default.png1000629.3 ns1.00-
f9c93b4c71314c7872e38461dc8006d4.png

这里的性能提升并不像前面的例子那么突出,但还是非常高的!

3. Order

接下来是这是新增了两个排序方法Order()OrderDescending()。当你不想映射到IComparable类型时,应该使用新的方法取代.NET7 中旧的OrderBy()OrderByDescending()方法。

[Params(1000)]
public int Length { get; set; }private double[] arr;[GlobalSetup]
public void GlobalSetup()
{var random = new Random();arr = Enumerable.Range(0, Length).Select(_ => random.NextDouble()).ToArray();
}[Benchmark]
public double[] OrderBy() => arr.OrderBy(d => d).ToArray();#if NET7_0
[Benchmark]
public double[] Order() => arr.Order().ToArray();
#endif
方法数组长度平均值风波无
OrderBy100051.13 μs27.61 KB
Order100050.82 μs19.77 KB

在这个基准中,只使用了.NET 7,因为Order()方法在旧的运行时中不可用。

我们无法看到这两种方法之间的性能影响。然而,我们可以看到的是在堆内存分配方面有很大的改进,这将显著减少垃圾收集,从而节省一些 GC 时间。

System.IO

在.NET 7 中,Windows 下的 IO 性能有了些许改善。WriteAllText()方法不再使用那么多分配的内存,ReadAllText()方法与.NET 6 相比也快了一些。

[Benchmark]
public void WriteAllText() => File.WriteAllText(path1, content);[Benchmark]
public string ReadAllText() => File.ReadAllText(path2);
方法运行时平均值比率分配
WriteAllTextoutside_default.png193.50 μs1.0310016 B
WriteAllTextoutside_default.png187.32 μs1.00464 B





ReadAllTextoutside_default.png23.29 μs1.0824248 B
ReadAllTextoutside_default.png21.53 μs1.0024248 B

序列化 (System.Text.Json)

来自System.Text.Json命名空间的JsonSerializer得到了一个小小的升级,一些使用了反射的自定义处理程序会在幕后为你缓存,即使你初始化一个JsonSerialzierOptions的新实例。

private JsonSerializerOptions options = new JsonSerializerOptions();
private TestClass instance = new TestClass("Test");[Benchmark(Baseline = true)]
public string Default() => JsonSerializer.Serialize(instance);[Benchmark]
public string CachedOptions() => JsonSerializer.Serialize(instance, options);[Benchmark]
public string NoCachedOptions() => JsonSerializer.Serialize(instance, new JsonSerializerOptions());public record TestClass(string Test);

在上面代码中,对NoCachedOptions()的调用通常会导致JsonSerialzierOptions的额外实例化和一些自动生成的处理程序。在.NET 7 中这些实例是被缓存的,当你在代码中使用这种方法时,你的性能会好一些。否则,无论如何都要缓存你的JsonSerialzierOptions,就像在CachedOptions例子中,你不会看到很大的提升。

方法运行时平均值比率分配分配比率
Defaultoutside_default.png135.4 ns1.04208 B3.71
CachedOptionsoutside_default.png145.9 ns1.12208 B3.71
NoCachedOptionsoutside_default.png90,069.7 ns691.897718 B137.82
Defaultoutside_default.png130.2 ns1.0056 B1.00
CachedOptionsoutside_default.png129.8 ns0.9956 B1.00
NoCachedOptionsoutside_default.png533.8 ns4.10345 B6.16

基本类型

1. Guid 相等比较

有一项改进,肯定会导致现代应用程序的性能大增,那就是对Guid相等比较的新实现。

private Guid guid0 = Guid.Parse("18a2c952-2920-4750-844b-2007cb6fd42d");
private Guid guid1 = Guid.Parse("18a2c952-2920-4750-844b-2007cb6fd42d");[Benchmark]
public bool GuidEquals() => guid0 == guid1;
方法运行时平均值比率
GuidEqualsoutside_default.png1.808 ns1.49
GuidEqualsoutside_default.png1.213 ns1.00

可以感觉到,新的实现也使用了 SIMD,比旧的实现快 30%左右。

65d06777094b0ce292198c968aceffd0.png

由于有大量的 API 使用Guid作为实体的标识符,这肯定会积极的产生影响。

2. BigInt 解析

一个很大的改进发生在将巨大的数字从字符串解析为BigInteger类型。就我个人而言,在一些区块链项目中,我曾使用过BigInteger类型,在那里有必要使用这种类型来表示 ETH 代币的精度。所以在性能方面,这对我来说会很方便。

private string bigIntString = string.Concat(Enumerable.Repeat("123456789", 100000));[Benchmark]
public BigInteger ParseBigInt() => BigInteger.Parse(bigIntString);
方法运行时平均值比率分配
ParseBigIntoutside_default.png2.058 s1.622.09 MB
ParseBigIntoutside_default.png1.268 s1.002.47 MB
733a8bc07f96fe6cce7e0b12d01ec46e.png

我们可以看到性能有了明显的提高,不过我们也看到它比.NET6 上多分配一些内存。

3. Boolean 解析

对于解析boolean类型,我们也有显著的性能改进:

[Benchmark]
public bool ParseBool() => bool.TryParse("True", out _);
方法运行时平均值比率
ParseBooloutside_default.png8.164 ns5.21
ParseBooloutside_default.png1.590 ns1.00
a3c68cc7781e9e674fa06e7a3ebb15f3.png

诊断

System.Diagnostics命名空间也进行了升级。进程处理有两个重大改进,Stopwatch有一个新功能。

1. GetProcessByName

[Benchmark]
public Process[] GetProcessByName()=> Process.GetProcessesByName("dotnet.exe");
方法运行时平均值比率分配分配比率
GetProcessByNameoutside_default.png2.065 ms1.04529.89 KB247.31
GetProcessByNameoutside_default.png1.989 ms1.002.14 KB1.00

新的GetProcessByName()的速度并不明显,但使用的分配内存比前者少得多。

7bd991cf56011860080c5cd6244c9f9f.png

2. GetCurrentProcessName

[Benchmark]
public string GetCurrentProcessName()=> Process.GetCurrentProcess().ProcessName;
方法运行时平均值比率分配分配比率
GetCurrentProcessNameoutside_default.png1,955.67 μs103.023185 B6.98
GetCurrentProcessNameoutside_default.png18.98 μs1.00456 B1.00

在这里,我们可以看到一个更有效的内存方法,对.NET 7 的实现有极高的性能提升。

c4d3d4a8cdf0c2c2779c8ee7f0bdee9a.png

3. Stopwatch

Stopwatch被广泛用于测量运行时的性能。到目前为止,存在的问题是,使用Stopwatch需要分配堆内存。为了解决这个问题,dotnet 社区实现了一个静态函数GetTimestamp(),它仍然需要一个复杂的逻辑来有效地获得时间差。现在又实现了另一个静态方法,名为GetElapsedTime(),在这里你可以传递之前的时间戳,并在不分配堆内存的情况下获得经过的时间。

[Benchmark(Baseline = true)]
public TimeSpan OldStopwatch()
{Stopwatch sw = Stopwatch.StartNew();return sw.Elapsed;
}[Benchmark]
public TimeSpan NewStopwatch()
{long timestamp = Stopwatch.GetTimestamp();return Stopwatch.GetElapsedTime(timestamp);
}
MethodMeanRatioAllocatedAlloc Ratio
OldStopwatch39.44 ns1.0040 B1.00
NewStopwatch37.13 ns0.94-0.00
1be1f363f23d845e2b4deb8def5afba7.png

这种方法的速度优化并不明显,然而节省堆内存分配可以说是值得的。

结尾

我希望,我可以在性能和基准测试的世界里给你一个有趣的切入点。如果你关于特定性能主题想法,请在评论中告诉我。

如果你喜欢这个系列的文章,请务必关注我,因为还有很多有趣的话题等着你。

谢谢你的阅读!

版权

原文版权:Tobias Streng
翻译版权:InCerry 

原文链接: https://medium.com/@tobias.streng/net-performance-series-1-performance-improvements-in-net-7-fb793f8f5f71

参考资料

[1]

CHUTTERSNAP: https://unsplash.com/@chuttersnap?utm_source=medium&utm_medium=referral

[2]

Unsplash: https://unsplash.com/?utm_source=medium&utm_medium=referral

[3]

dotnet社区: https://github.com/microsoft/dotnet

[4]

BenchmarkDotNet: https://benchmarkdotnet.org/articles/overview.html

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

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

相关文章

UVA - 10061 How many zero#39;s and how many digits ?

n!x*b^y, 当x为正整数时,最大的y就是n!末尾0的个数了, 把n,b分别拆成素因子相乘的形式: 比如, n5,b16 n5,b2^4, 非常明显,末尾0的个数为0 10进制时,n!a*10^x b进制时,n!c*b^y 非常明显,n!的位数就是最大的x1 这里计算我用了log,精度设置为1e-9 #include<iostream> #inclu…

丁洪波 -- 不要“ 总是拿着微不足道的成就来骗自己”

都市快报实盘大赛25期&#xff1a;于海飞/丁洪波荣获冠亚军 七禾网 时间&#xff1a;2010-11-02 12:47:05 来源&#xff1a;期货中国10月30日下午&#xff0c;2010年浙商期货实盘大赛第三季度&#xff08;都市快报实盘大赛第25期&#xff09;颁奖典礼在天科大厦浙商期货大会议室…

面试专题(Mysql及Mongodb)

2019独角兽企业重金招聘Python工程师标准>>> mysql面试题 1. 各个数据库存储引擎区别 mysql的存储引擎是针对表进行设置的&#xff0c;一个库的不同表可以设置不同的存储引擎&#xff0c;mysql默认支持多种存储引擎&#xff0c;以适用不同领域的数据库应用需要&…

织梦网站翻页php,dedecms织梦网站列表页和内容页分页样式

织梦分页标签{dede:pagelist istitem"index,pre,next,end,option,info," listsize"5"/}&#xff0c;{dede:prenext getpre/}&#xff0c;{dede:prenext getnext/}。默认样式和使用模板css样式布局不一样,这时又不想重写样式&#xff0c;我们可以修改织梦标…

通过中间件添加用户的Claim

本文主要介绍 Sang.AspNetCore.RoleBasedAuthorization[1] 库如何通过中间件实现对用户 Claim 的添加。背景前面我们介绍了通过对自定义授权策略和自定义授权处理程序的使用实现了基本的RBAC权限设计&#xff0c;将大量的用户可访问资源及操作的标识直接放到用户的 JWT Token 中…

部署也是工程的一部分,也要编程(自动化)

部署和开发一样&#xff0c;同样面临变化。同样有复杂的细节。 同样应该代码化&#xff0c;自动化。把复杂性、思路&#xff0c;操作&#xff0c;都固化下来&#xff0c;显式表达。 不要“雪花”式配置。 把最近看的文章摘抄一下 集句&#xff1a; 1频繁做让你感到痛苦的事情&a…

KDD走进阿里 数百专家聚集探讨产学研一体化

6月29日&#xff0c;由阿里巴巴集团、中国中文信息学会、KDD China联合主办的数据挖掘前沿发展与未来论坛在杭州举行&#xff0c;会议吸引了来自国际顶级高校和知名企业的近300名专家学者到场参会、近30000人在线观看。论坛除了分享最新的数据挖掘领域最新科研成果及研发思路外…

zookeeper学习03 使用场景

zookeeper实际应用场景 zookeeper能够实现哪些场景 1&#xff09;订阅发布/配置中心 watcher机制 统一配置管理&#xff08;disconf&#xff09; 实现配置信息的集中式原理和数据的动态更新 实现配置中心有俩种模式&#xff1a;push,pull 长轮询 zookeeper采用的是推拉相结合的…

php模板引擎循环start,PHP模板引擎Smarty内建函数section,sectionelse用法详解

本文实例讲述了PHP模板引擎Smarty内建函数section,sectionelse用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;section 是 Smarty 模板中除了 foreach 以外的另一种处理循环的方案&#xff0c;section 比 foreach 要灵活&#xff0c;就像是一个改进的 foreach 语句…

OpenHarmony操作系统与龙芯2K1000LA芯片完成适配,龙架构平台获得开源鸿蒙认证

近日&#xff0c;龙芯中科与软通动力控股公司鸿湖万联共同完成OpenHarmony操作系统与龙芯2K1000LA处理器的适配&#xff0c;“乘风1000”开发板&#xff08;搭载龙芯2K1000LA&#xff09;荣获OpenHarmony生态产品兼容性证书。至此&#xff0c;万物互联的OpenHarmony生态体系再次…

struts2开发action 的三种方法以及通配符、路径匹配原则、常量

struts2开发action 的三种方法 1、继承ActionSupport public class UserAction extends ActionSupport {// Action中业务处理方法public String login() {System.out.println("UserAction.login()"); // return "success";return SUCCESS;} } 2、实现…

闭包--闭包作用之保护(一)

闭包作用:保护 形成私有作用域,保护里面的私有变量不受外界干扰例如多人协作开发&#xff1a;A的代码有fn(),B的代码有fn(),但是他们不相互影响 // A的代码<script>(function() {function fn1() {console.log("aa")}window.fn1 fn1;})()// window.fn1() //11&…

left join 和 inner join

2019独角兽企业重金招聘Python工程师标准>>> left join 和 inner join 首先 MySQL 中 inner join 的效率确实要高于 left join。所以没必要使用 left join 转弯成 inner join 的效果。这样不但效率降低&#xff0c;可读性也会降低。 Number1 select from t1 left j…

oracle 数据库中拆分,oracle数据库字符串拆分

第一种 直接返回切分的字符串create or replace function Get_StrArrayLength(av_str varchar2,--要分割的字符串av_split varchar2 --分隔符号)return numberislv_str varchar2(1000);lv_length number;beginlv_str:ltrim(rtrim(av_str));lv_length:0;while instr(lv_str,av_s…

Vue3+.NET6,轻松开发管理后台!(可复用)

在GitHub是没找到简单好用的Vue3.NET6管理后台项目&#xff0c;有收藏的请评论区分享。这里分享一套Vue3 Axios TS Vite Element Plus .NET 6 WebAPI JWT SqlSugar的通用管理后台&#xff0c;前后端分离架构&#xff0c;各种最新框架组件&#xff0c;实现了管理后台几乎…

iOS网络请求安全认证(JWT,RSA)

在网络世界中&#xff0c;安全是一个很重要的问题&#xff0c;以往的HTTP请求已经不能承担这个安全任务&#xff0c;抓包工具一抓&#xff0c;你的所有网络请求全都曝光。当然&#xff0c;你可能会采用加密算法来加密数据&#xff0c;但是这仍然不够。 在移动端和服务器的通信过…

微信小程序黑客马拉松即将开始,来做最酷的 Mini Program Creators!

微信小程序黑客马拉松正式启动 近日&#xff0c;小程序斩获一项世界级殊荣——作为一项全新的技术和应用创新&#xff0c;小程序首次获选世界互联网领先科技成果。目前小程序应用数量已超过 100 万&#xff0c;覆盖了 200 多个细分行业&#xff0c;日活用户达到 2 亿。 微信小程…

oracle 文件写 n r,[oracle]log_archive_dest_n与DB_RECOVERY_FILE_DEST

DB_RECOVERY_FILE_DEST参数是默认的flashrecovery area的路径&#xff0c;里面存放有归档日志、闪回日志以及rman的备份文件等文件。LOG_ARCHIVE_DEST_n参数是存放归档日志的路径&#xff0c;n表示1~10的一个整数&#xff0c;由于归档日志在recovery的时候担当了重要的角色&…

记一次 .NET 某娱乐聊天流平台 CPU 爆高分析

一&#xff1a;背景 1.讲故事前段时间有位朋友加微信&#xff0c;说他的程序直接 CPU100%&#xff0c;每次只能手工介入重启&#xff0c;让我帮忙看下到底怎么回事&#xff0c;哈哈&#xff0c;这种CPU打满的事故&#xff0c;程序员压力会非常大, 我让朋友在 CPU 高的时候抓 2 …

linux下mariadb大小写敏感

2019独角兽企业重金招聘Python工程师标准>>> Linux下安装好mariadb后&#xff0c;在使用时会发现mariadb对大小写敏感&#xff0c;这对开发带来一定的不利&#xff0c;这时只要在配置文件中配置一下&#xff0c;取消大小写敏感即可&#xff1a; sudo vi /etc/MySQL/…