性能是.NET Core的一个关键特性

关键要点

  • .NET Core是跨平台的,可运行在Windows、Linux、Mac OS X和更多平台上;与.NET相比,发布周期要短得多。大多数.NET Core 都是通过NuGet软件包交付的,可以很容易地发布和升级。

  • 更快速的发布周期对性能提升工作以及改进诸如SortedSet和LINQ . tolist()方法等语言结构性能的大量工作都有着特别的帮助。

  • 通过引入了System.ValueTuple和Span这样的类型,更快的周期和更容易的升级也为迭代改进 .NET Core性能的新想法带来了机会。

  • 这些改进之后可以反馈到完整的 .NET 框架中。

随着.NET Core2.0的发布,微软有了下一个主要版本的通用目标,模块化、跨平台和开源平台最初发布于2016年。.NET Core 已经创建了许多api,这些api可以在.NET 框架的当前版本中使用。它最初是为下一代ASP.NET创建的解决方案,但现在是驱动、是许多其他场景的基础,包括物联网、云和下一代移动解决方案。在本系列中,我们将探讨一些.NET Core的好处,以及它如何不仅能让传统的.NET开发人员受益,还能让所有需要为市场带来健壮、高性能和经济解决方案的技术人员受益。

现在,.NET Core正在路上,微软和开源社区可以在框架的新特性和增强上进行更快速的迭代。在.NET Core中,性能是持续关注的一个领域: .NET Core在执行速度和内存分配方面都带来了许多优化。

在这篇文章中,我们将讨论一些优化,以及如何在以后的性能工作中更多地使用连续流或Span<T>,为我们的开发人员生活带来帮助。

.NET 和.NET Core

在深入研究之前,让我们先看看完整的.NET框架(为方便起见我们称之为.NET)和.NET Core之间的主要区别。为了简化问题,让我们假设两个框架都遵循.NET标准,它本质上是一个规范,定义了所有.NET的基类库基线。这使得两个世界非常相似,除了两个主要的区别:

首先,.NET主要是在Windows上的,而.NET Core是跨平台的,可运行在Windows、Linux、Mac OS X和更多平台上。第二,发布周期非常不同。.NET作为一个完整的框架安装程序,它是系统范围的,通常是Windows安装的一部分,使发布周期更长。对于.NET Core,在一个系统上可以有多个.NET Core安装,而且没有长时间的发布周期:大多数.NET Core是以NuGet包交付的,可以轻松地发布和升级。

最大的优点是.NET Core世界可以更快地迭代并文学地尝试新概念,并最终将它们反馈到完整的.NET框架中,作为未来.NET标准的一部分。

经常(但不总是),.NET Core的新特性是由c#语言设计驱动的。因为框架可以更快地进化,语言也可以。一个快速发布周期和性能增强的主要例子是System.ValueTuple。c#和VB.NET 15引入了“值元组”,这很容易添加到.NET Core,因为更快的发布周期,并且针对完整的.NET 4.5.2和更早的版本,可以作为一个NuGet包用于完整的.NET,在.NET 4.7中也可以仅成为完整的.NET框架的一部分。

现在让我们来看看其中的一些性能和内存改进。

.NET Core的性能改进

.NET Core工作的优点之一是,许多东西要么需要重新构建,要么需要从完整的.NET框架中移植。让所有的内部构件在flux中运行一段时间,再加上快速发布周期,提供了一个在代码中进行一些性能改进的机会,以前,这些性能改进几乎被认为是“不要碰,它刚刚正常工作!“。

让我们从SortedSet和它的Min和Max的实现开始。SortedSet是通过利用自平衡树结构,以有序顺序维护的对象集合。在此之前,从该集合中获取最小或最大对象需要向下遍历树(或向上),调用每个元素的委托,并将返回值设置为当前元素的最小值或最大值,最终到达树的顶部或底部。调用该委托并传递对象意味着有相当多的开销。直到有一个开发人员看到了这棵树,并删除了不需要的委托调用,因为它没有提供任何值。他自己的基准测试显示有30%-50%的性能提升。

另一个很好的例子是在LINQ中,在常用的. tolist()方法中更具体。大多数LINQ方法在IEnumerable上作为扩展方法操作,以提供查询、排序和诸如. tolist()之类的方法。通过这样做,我们不必关心底层IEnumerable的实现,只要 能够遍历它就行了。

缺点是,当调用. tolist()时,我们不知道要创建的列表的大小,只枚举enumerable中的所有对象,这把即将返回的列表的大小增加了一倍。这有点愚蠢,因为它潜在地浪费了内存(和CPU周期)。因此,如果底层IEnumerable实际上是具有已知大小的列表或数组,那么就会更改为创建一个已知大小的列表或数组。来自.NET团队的基准测试显示,这些数据的吞吐量增加了4倍。

当查看GitHub上CoreFX实验室存储库中的pull请求时,我们可以看到微软和社区都做出了大量的性能改进。因为.NET Core是开源的,你也可以提供性能修正。其中大多数都是:对.NET中的现有类进行修复。但还有更多:.NET Core还介绍了一些关于性能和内存的新概念,这些概念不仅仅是修复这些现有的类。让我们来看看本文其余部分的内容。

减少使用System.ValueTuple的分配

假设我们想从一个方法返回多个值。以前,我们要么使用out参数,这让人用起来非常不爽,而且在编写async方法时也不支持。另一种选择是使用System.Tuple作为返回类型,但它分配了一个对象,并且具有相当不友好的属性名称(Item1, Item2,…)。第三种选择是使用特定类型或匿名类型,但是在编写代码时这种做法会引入开销,因为我们需要定义类型,而且如果我们需要的是嵌入在该对象中的值,它也会造成不必要的内存分配。

遇到元组返回类型,由System.ValueTuple支持。c# 7和VB.NET 15添加了一个语言特性,可以从一个方法返回多个值。下面是之前和之后的示例:

//之前:
private Tuple<string, int> GetNameAndAge(){return new Tuple<string, int>("Maarten", 33);}

//之后:
private (string, int) GetNameAndAge(){return ("Maarten", 33);}

在第一个例子中,我们分配一个元组。因为几乎不必做什么额外的工作,分配是在托管堆上完成的,在某个时刻,垃圾回收(GC)将不得不清理它。在第二种情况下,编译器生成的代码使用的是ValueTuple类型,它本身就是一个struct,并在堆栈上创建,这使我们能够访问我们想要处理的两个值,同时确保在包含的数据结构上不需要做垃圾回收。

如果我们使用ReSharper的中间语言(IL)查看器查看编译器在上面示例中生成的代码,那么就会很明显看到这种差异。这里有两个方法签名:

//之前:.method 
private hidebysig instance class [System.Runtime]System.Tuple`2<string, int32>   GetNameAndAge() cil managed {// ...}//之后:.method
private hidebysig instance valuetype [System.Runtime]System.ValueTuple`2<string, int32> GetNameAndAge() cil managed {// ...}

我们可以清楚地看到第一个示例返回一个类的实例,第二个例子返回一个值类型的实例。类是在托管堆中分配的(由CLR跟踪和管理,并受垃圾收集的管制,是可变的),而值类型分配在堆栈上(速度快且较少的开销,是不可变的)。简而言之: System.ValueTuple本身并没有被CLR跟踪,它只是作为我们关心的嵌入值的一个简单容器。

请注意,在其优化的内存使用情况下,像元组解构这样的特性是非常令人愉快的副产品,它使这部分语言和框架都成为了这一部分。

使用Span<T>减少子字符串的内存分配

在前一节中,我们已经讨论了栈和托管堆。大多数.NET开发人员只使用托管堆,但.NET有三种类型的内存可供使用,这取决于具体情况:

  • 栈内存——我们通常分配的值类型的内存空间,比如int, double, bool,……它非常快(通常在CPU的缓存中使用),但大小有限(通常小于1 MB)。富有挑战精神的开发人员会使用stackalloc关键字添加自定义对象,但要知道它们是有危险性的,因为在任何时间都可能发生StackOverflowException,使我们的整个应用程序崩溃。

  • 非托管内存——没有垃圾收集器的内存空间,我们必须自己使用像Marshal.AllocHGlobal 和Marshal.FreeHGlobal之类的方法预订和释放内存。

  • 托管内存/托管堆——垃圾收集器释放已经不再使用的内存空间,使我们大多数人都过着无忧无虑的程序员生活,很少有内存问题。

它们都有各自的优缺点,并有特定的用例。但是,如果我们想要编写一个与所有这些内存类型兼容的库该怎么办呢? 我们必须分别为他们提供方法。一个针对托管对象,另一个针对指针指向堆栈上或非托管堆上的对象。一个很好的例子就是创建一个字符串的子字符串。我们需要获取一个System.String并返回一个新System.String的方法,即要处理的托管版本的子字符串。非托管/堆栈版本将使用char*(是的,一个指针!)和字符串的长度,并返回类似的指向结果的指针。难以控制…

这个System.Memory NuGet包(目前仍是预览版)引入了一个新的Span<T>结构。它是一个值类型(因此没有被垃圾收集器跟踪),它试图统一对任何底层内存类型的访问。它提供了一些方法,但本质上是这样的:

  • 一个T的引用

  • 一个可选的开始索引

  • 一个可选的长度

一些实用函数可以抓取一个Span<T>的切片,复制内容,…

把它想成这个(伪代码):

public struct Span<T>{ref T _reference;int _length;public ref T this[int index] { get {...} }}

不管我们是使用字符串、char[]甚至是未管理的char*来创建一个Span<T>, Span<T>对象都提供了相同的函数,比如返回索引中的元素。可以把它看作是T[],其中T可以是任何类型的内存。如果我们想要编写一个子Substring()方法来处理所有类型的内存,那么我们所要关心的就是正在使用的Span<char>是如何工作的 (或者它的不可变版本,ReadOnlySpan<T>):

ReadOnlySpan<char> Substring(ReadOnlySpan<char> source, int startIndex, int length);

这里的source 参数可以是基于System.String的span,或者是未管理的char*,我们不需要关心。

而是让我们暂时忘掉内存类型不可知的方面,并关注性能。如果我们要为System.String编写一个Substring()方法,我们可能会想到的:

string Substring(string source, int startIndex, int length)

string Substring(string source, int startIndex, int length){var result = new char[length];for (var i = 0; i < length; i++){result[i] = source[startIndex + i];}return new string(result);}

这很好,但实际上我们正在创建子字符串的副本。如果我们调用Substring(“Hello World!”,0,5),我们在内存中有两个字符串: “Hello World”和“Hello”可能会浪费内存空间,我们的代码仍然需要将数据从一个数组复制到另一个数组,以实现这一点,消耗了CPU周期。我们的实现并不坏,但也不理想。

想象一下一个web框架的实现,它使用上面的代码从一个包含header和body的HTTP请求中获取请求体。我们必须分配具有重复数据的大块内存:一个具有整个传入请求的内存和一个仅包含请求体的子字符串。然后是需要从原始字符串复制数据到子字符串的开销。

现在,让我们用 (ReadOnly)Span<T>来重写它:

static ReadOnlySpan<char> Substring(ReadOnlySpan<char> source, int startIndex, int length){return source.Slice(startIndex, length);}

好吧,它变短了,但其实还有更多变化。由于实现了方法Span<T>,所以我们的方法不返回源数据的副本,而是返回引用源的子集的Span<T>。或者在将HTTP请求拆分为header和body的例子中:我们有3个Span:传入的HTTP请求,指向原始数据的头部分的一个span<T>,指向请求体的另一个Span<T>。数据在内存中只有一份(创建第一个Span的数据),其他所有的数据只会指向原始数据的切片。没有重复数据,没有复制和复制数据的开销。

结论

随着.NET Core和更快的发布周期,微软和.NET Core的开源社区可以在与性能相关的新特性上快速迭代。我们已经看到框架中很多改进现有代码和构造的工作,比如改进LINQ的. tolist()方法。

更快的周期和更容易的升级也带来了迭代改进.NET Core性能的新想法的机会,通过引入诸如System.ValueTuple and Span<T>之类的类型,使. net开发人员更自然地使用我们在运行时可用的不同类型的内存,同时避免与它们相关的常见缺陷。

想象一下,如果一些.net基类被重写为Span<T>实现,诸如字符串UTF解析、加密操作、web解析和其他典型的CPU和内存消耗任务。这将对框架带来很大的改进,并且所有的. net开发人员都将受益。事实证明,这正是微软计划要做的事情! .NET Core的性能前景光明!

关于作者

Maarten Balliauw喜欢构建web和云应用程序。他的主要兴趣是ASP.NET MVC、 c#、Microsoft Azure、 PHP和应用程序性能。他与别人共同创立了MyGet,他还是JetBrains的开发人员。他是微软Azure的ASP内部人员和MVP。Maarten在不同的国家和国际活动中经常发言,并在比利时组织Azure用户组活动。在业余时间,他自己酿造啤酒。Maarten的博客。

 

随着.NET Core2.0的发布,微软有了下一个主要版本的通用目标,模块化、跨平台和开源平台最初发布于2016年。.NET Core 已经创建了许多api,这些api可以在.NET 框架的当前版本中使用。它最初是为下一代ASP.NET创建的解决方案,但现在是驱动、是许多其他场景的基础,包括物联网、云和下一代移动解决方案。在本系列中,我们将探讨一些.NET Core的好处,以及它如何不仅能让传统的.NET开发人员受益,还能让所有需要为市场带来健壮、高性能和经济解决方案的技术人员受益。

相关文章:

  • Azure和.NET Core成就天作之合

  • .Net Core中使用ref和Span<T>提高程序性能

原文地址: http://www.infoq.com/cn/articles/performance-net-core


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

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

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

相关文章

简单的数学问题 洛谷 P1414 又是毕业季II

题意 给出n个数字&#xff0c;询问从中挑出k个数字&#xff0c;最大可能的公共gcd是多少。 题解 k个数的gcd一定出现至少k次。 我们遍历所有的数并对每个数字求它所有的因子&#xff0c;统计各个因子出现的个数。 给定k时&#xff0c;我们的答案就是所有出现次数>k的因…

【枚举】连续自然数和(jzoj 2102)

连续自然数和 题目大意&#xff1a; 输出一个n&#xff0c;求出所有相加等于n的连续自然数序列 样例输入 10000 样例输出 18 142 297 328 388 412 1998 2002 数据范围限制 10 < M < 2,000,000 解题思路&#xff1a; 这道题的正解的时间复杂度是o(2n)o\left …

nssl1460-逛机房【bfs】

正题 题目大意 nnn次询问&#xff0c;给出一个数xxx&#xff0c;每次可以进行操作 修改其中一个位&#xff0c;去掉前导零删掉其中一个位&#xff0c;去掉前导零 询问最少步骤使得xxx变为一个完全平方数 解题思路 我们可以从完全平方数开始广搜&#xff0c;操作变为 加入一…

将 ASP.NET Core 2.0 项目升级至 ASP.NET Core 2.1 RC 1

微软发布了 .NET Core 2.1 RC 1 &#xff0c;虽然只是 Release Candidate 版&#xff0c;但已经可以在生产环境中使用。NET Core 2.1 RC is supported by Microsoft and can be used in production..NET Core 2.1 的性能大提升早让人垂涎三尺&#xff0c;看到这个消息后&#x…

洛谷P1169 树上分组背包

题解 第一次写树上分组背包的题目。 什么是分组背包&#xff1f; 分组背包就是将物品进行分组每组内部只能选择一类物品。 for(int i 1;i < N;i){for(int j 0;j < V;j){for(int k 0;k < item[I];k){dp[i][j] max(dp[i][j],dp[i-1][j-v[i][k]]w[i][k]); }} …

【dfs】简单游戏(jzoj 2121)

简单游戏 题目大意 原本有n个数字&#xff0c;第1,2个相加&#xff0c;第2&#xff0c;3个相加……第n-1,n个相加&#xff0c;由此得出一个长度为n-1的新序列&#xff0c;然后不停重复&#xff0c;最后得出一个t&#xff0c;现在给出一开始的n和t求符合的序列&#xff08;字典…

nssl1459-空间简单度【扫描线,线段树】

正题 题目大意 nnn个点的一颗树&#xff0c;合法路径定义为一条路径上每个点的编号相差大于KKK。求合法路径数 解题思路 首先我们可以求不合法的路径数&#xff0c;这样我们就有了K∗nK*nK∗n个不合法&#xff08;即不能在同一个路径上&#xff09;的点对。 然后这题就和之前…

ASP.NET Core Identity Hands On(2)——注册、登录、Claim

上一篇文章&#xff08;ASP.NET Core Identity Hands On&#xff08;1&#xff09;——Identity 初次体验&#xff09;中&#xff0c;我们初识了Identity&#xff0c;并且详细分析了AspNetUsers用户存储表&#xff0c;这篇我们将一起学习Identity 默认生成的样板代码的注册与登…

洛谷 动态规划一日游 P2577、P1070、P2051

记 2018年3月19日 贼颓呢&#xff0c;一天就写了两道DP&#xff0c;还都不会写&#xff0c;这可GG。 动态规划真的难且有趣&#xff0c;算法题中动态规划占到了很大的比例&#xff0c;而且动态规划往往是辅助解决一些其他类型问题的基础&#xff0c;加深加强对动态规划问题的…

【DP】【高精】幸运票 (jzoj 2122)

幸运票 题目大意&#xff1a; 一个长度为2N的序列&#xff0c;这些数的总和为S&#xff0c;当这个序列的前N个和后N个总和相等时&#xff0c;它是符合题意的&#xff0c;问有符合题意的有多少种可能 样例输入 2 2 样例输出 4 数据范围限制 1<N<50 S<1000 解…

51nod-有限背包计数问题【dp】

正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId1597 题目大意 nnn种物品&#xff0c;第iii个大小为iii且有iii个。 求恰好填满大小为nnn的背包的方案数 解题思路 我们可以将背包分为两份&#xff0c;对于大小小于等于n\sqrt nn​的物品&#xff0c;这…

Build 2018大会:.NET概述和路线图

在Microsoft Build 2018大会上&#xff0c;.NET项目管理主管Scott Hunter和.NET社区主管Scott Hanselman举行了一场有关.NET未来发展的会谈。会谈指出&#xff0c;未来.NET平台将可以搭建任何类型的应用程序&#xff1a;桌面程序、web程序、云程序、移动应用、游戏应用、物联网…

洛谷P2278操作系统 模拟+堆

一道模拟题 竟然活生生的不会模拟&#xff0c;感觉自己好菜啊。 在模拟的时候&#xff0c;一定要弄清楚要对什么进行模拟。 题解 进程的等待队列是一个优先队列&#xff0c;优先队列是以优先级降序作为第一关键字&#xff0c;以进入时间为第二关键字。在操作系统这道题目中…

【二分】抄书 (jzoj 2123)

抄书 题目大意&#xff1a; 有n本书&#xff0c;分给m个人抄&#xff0c;每个人只能拿到连续的书&#xff08;不能把一本书分开&#xff09;&#xff0c;问抄书最多的人要抄多少页 样例输入 9 3 100 200 300 400 500 600 700 800 900 样例输出 1700 数据范围限制 对于…

nssl1467-U【差分】

正题 题目大意 n∗nn*nn∗n的矩阵&#xff0c;每次让一个下三角形内数字加上一定权值。求最后所有位置的异或和 解题思路 我们发现如果我们对于没行做前缀和的话&#xff0c;我们需要修改的位置就是一个竖直下去的一列和斜着的一条&#xff0c;所以我们可以分别对于竖着的和斜…

汽车之家汽车品牌Logo信息抓取 DotnetSpider实战[三]

一、正题前的唠叨第一篇实战博客&#xff0c;阅读量1000&#xff0c;第二篇&#xff0c;阅读量200&#xff0c;两篇文章相差近5倍&#xff0c;这个差异真的令我很费劲&#xff0c;截止今天&#xff0c;我一直在思考为什么会有这么大的差距&#xff0c;是因为干货变少了&#xf…

洛谷P1801 黑匣子 双堆套路的使用

题意 题目链接 题解 这道题本可以用Treap暴力求解出来&#xff0c;但是不够优雅&#xff0c;因为没有充分利用到题目中给的条件&#xff0c;那就是要求的ithith小的值的ii是单调递增的。我们用两个堆来维护,大顶堆和小顶堆。 大顶堆中的元素是排好序的前i&#x2212;1&qu…

2019.01.26【NOIP普及组】模拟赛C组总结

总结 这次比赛的得分是&#xff1a;10001060170 第一题想了一会&#xff0c;想到了方法&#xff0c;直接打出来&#xff0c;第二题不会&#xff0c;想水分&#xff0c;但没水到&#xff0c;第三题打了一个假的DP&#xff0c;10分&#xff0c;第四题用DP超时了&#xff0c;60分…

nssl1468-V【状压,数学期望,dfs】

正题 题目大意 nnn个球排成一排颜色不同&#xff0c;每次选择一个随机的[1..n][1..n][1..n]中的xxx&#xff0c;然后删掉第xxx个或第n−x1n-x1n−x1个数&#xff0c;求删kkk次之后删掉的白球最多&#xff0c;求删掉数量的期望值 解题思路 考虑状态压缩dpdpdp&#xff0c;定义第…

洛谷 一种堆套路 P1631序列合并、P2085最小函数值

题目链接 序列合并 最小函数值 题解 这两道题做法基本一样&#xff0c;是使用同一种套路解决的&#xff0c;这里用序列合并来举例说明。 序列合并要求出N2N2个和中最小的N个数。 我们用一个堆来维护我们需要的数&#xff0c;并且保证当前最小值一定在堆中。 把a和b排个序…