编写高性能的C#代码(三)使用SPAN

原文来自互联网,由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权,请联系小编,小编将在24小时内删除。

作者介绍:

史蒂夫·戈登(Steve Gordon)是Microsoft MVP,Pluralsight的作者,布莱顿(英国西南部城市)的高级开发人员和社区负责人。

编写高性能的C#代码(三)使用SPAN 

这篇文章继续了我有关编写高性能C#代码的系列文章[1]。在本文中,我们将通过介绍Span 类型从上两篇文章继续,并通过将其转换为基于Span的版本来重构一些现有代码。我们将使用Benchmark.NET比较这些方法并验证我们的更改是否改进了代码。

如果您想遵循示例代码,可以在GitHub上找到[2]

什么是SPAN ?

Span是C#7.2引入的一种新类型,在.NET Core 2.1运行时中受支持。现有的.NET Standard 1.0运行时都有一个.NET Standard实现,但是在.NET Core中,我将重点介绍运行时更改,以支持可能的最佳版本,也称为“fast span”。

Span提供对内存连续区域的类型安全访问。该内存可以位于堆,堆栈上,甚至可以由非托管内存组成。Span具有相关的类型ReadOnlySpan ,该类型提供内存中数据的只读视图。ReadOnlySpan可用于查看不可变类型(例如字符串)占用的内存。我更喜欢将Span视为进入某些现有内存的窗口,而不管其分配在何处。

图片

在上图中,Span 引用一些已经分配的连续内存。现在,我们在该内存上有了一个窗口。

Span 被定义为引用结构,这意味着它仅限于仅在堆栈上分配。这减少了一些潜在的用例,例如将其存储为类中的字段或在异步方法中使用它。这些限制可以通过使用类似的新型Memory来解决,我们将在以后的文章中介绍它。引用结构设计的主要原因是要确保在使用Span时,我们不会引起其他堆分配。这是它支持高性能代码路径中如此高度优化的用例的原因之一。

我将避免为这篇文章过多地介绍实现细节(毕竟这是一篇介绍),而将重点放在一个示例中,我们可能在哪里使用它以及它如何影响我们的基准。

如果您想阅读有关Span的更多详细信息,我建议以下链接:

•Span 结构[3]•有关Span的所有信息:探索新的.NET主体[4]•C#7.2:了解Span[5]•Span By Adam Sitnik[6]

加快现有代码的速度并减少分配

在上一篇文章中,我们对一些代码进行了基准测试,这些代码用于从全名字符串中“解析”姓氏。通过Benchmark.NET,我们确定该方法需要125.8 ns的时间运行,并且每次运行分配160个字节。

在使用基于Span 的方法进行重构之前,我希望这是一个公平的竞争,因此我将首先不使用Span 来优化代码。这有望成为一个很好的例子,因为它着重指出,即使不使用Span之类的新功能,也可以通过对正在执行的工作进行一些思考来优化现有代码。

当前代码在任何空格上分割字符串,这将组成一个字符串数组。如果考虑到这一点,我们将分配一个数组,在使用名称“ Steve J Gordon”的情况下,这样做时将分配三个较小的字符串“ Steve”,“ J”和“ Gordon”。正如我们在基准测试中所看到的那样,这会导致分配160个字节。

对于查找姓氏的要求,我们不在乎存储名称的所有部分,而只是存储我们希望是姓氏的最后一部分。请注意,在此示例中,我忽略了多词姓氏等情况!

让我们向NameParser添加另一个方法,该方法而不是拆分字符串,而是获取最后一个空格字符的索引,并使用该方法获取代表姓氏的子字符串。

public string GetLastNameUsingSubstring(string fullName){var lastSpaceIndex = fullName.LastIndexOf(" ", StringComparison.Ordinal);return lastSpaceIndex == -1? string.Empty: fullName.Substring(lastSpaceIndex + 1);}

首先,我们获取全名字符串中最后一次出现空格的索引。如果它为-1,则找不到任何空格,因此我们将返回一个空字符串作为默认结果。如果找到索引,则使用Substring方法提取姓氏并将其返回。

我们稍后会将该版本包含在我们的基准测试中。但是,实际上,值得在进行代码改进的每个迭代时对其进行测试,以验证您是在改进方面还是使它们变得更糟。

使用SPAN 

让我们看看这次如何使用Span 重新编写此代码。在高性能需求旺盛的场景中,我们既要提高速度又要减少代码中的内存分配。

 public ReadOnlySpan<char> GetLastNameWithSpan(ReadOnlySpan<char> fullName){var lastSpaceIndex = fullName.LastIndexOf(' ');return lastSpaceIndex == -1 ? ReadOnlySpan<char>.Empty : fullName.Slice(lastSpaceIndex + 1);}

首先要注意的是,方法参数“ fullName”现在的类型为ReadOnlySpan 。某些类型(例如字符串)可以隐式转换为chars的ReadOnlySpan,因此此方法签名可以正常工作。现在,返回类型也是ReadOnlySpan。

首先,以与上面的优化代码非常相似的方式,我们寻找空格字符的最后一个索引。

同样,如果其值为-1,则我们找不到空格,并且将返回空的ReadOnlySpan 结果。

如果找到空格字符,我们现在可以使用Span的一种功能,即“切片”(Slice)。

切片是一项非常强大的操作,我们可以将现有的Span和“切片”放到更紧密的窗口中。切片时,我们为切片指定起始位置的索引,并为切片指定终止位置的长度。省略长度会导致从起始位置到Span结束的切片。

切片是一种低成本的操作,因为我们不复制任何内容,而只是创建一个新的Span,该Span表示一个进入现有内存范围子集的窗口。

图片

在上图中,我们可以创建原始Span的Slice来查看其中的5个元素,而无需分配原始内存的任何其他副本。

在新的基于Span的代码中,我们从空格字符后的索引处开始获取fullName的一部分。由于我们未指定长度,因此此切片将运行到现有Span的末尾。

对Span进行切片后,会在切片的部分上产生一个新的Span,然后将其作为方法的结果返回。

至此,我们有两个潜在的改进代码版本,一个使用Substring,另一个使用Span 。让我们更新基准并比较结果。

衡量改进基准

添加两个新基准后,基准类现在如下所示:

[RankColumn][Orderer(SummaryOrderPolicy.FastestToSlowest)][MemoryDiagnoser]public class NameParserBenchmarks{private const string FullName = "Steve J Gordon";private static readonly NameParser Parser = new NameParser();[Benchmark(Baseline = true)]public void GetLastName(){Parser.GetLastName(FullName);}[Benchmark]public void GetLastNameUsingSubstring(){Parser.GetLastNameUsingSubstring(FullName);}[Benchmark]public void GetLastNameWithSpan(){Parser.GetLastNameWithSpan(FullName);}}

我们定义了三个基准,每个基准在NameParser中采用不同的方法。运行基准测试在我的计算机上给出以下结果…

图片

此列表中的最后一项是我们原始的GetLastName方法。因为我们要求获得排名结果,并且此方法运行的最慢,所以它在最后显示出来。

这次大约花了125ns的时间运行,当然仍然分配了160个字节。

第二快的是我们尝试在不使用Span 的情况下改进代码的情况,该代码使用Substring。此代码比原始方法快大约3倍。重要的是,我们现在将分配减少到只有40个字节。这说明了我们在调用子字符串时要分配的姓氏字符串。

总的赢家是基于Span 的方法。这比我们的原始代码快10倍,比基于子字符串的方法快2.8倍。

这里真正重要的是,因为我们要对Span进行切片以查找姓氏的位置,并且还返回Span作为方法的输出,所以我们永远不会分配新的字符串。通过已分配的内存状态(现在为空)可以明显看出这一点。

图片

对于单个调用,节省的160个字节(或子字符串方法节省40个字节)并不庞大,但是在特定场景下上,节省的费用加起来了。

如果此代码需要在我维护的每天处理约2000万条消息的数据处理服务中运行,那么我们每天将节省3.2 GB的分配。这些可能是短暂的分配,但是即使如此,它们仍将导致垃圾回收。根据估算的Gen 0 / 1k操作数(译者注,是指0代回收,每次回收1k字节),原始代码每天将触发2,000个操作,共506个GC。

这是CPU时间和暂停时间,我们可以通过避免分配任何资源来帮助减少时间。

摘要

在本文中,我们研究了新的Span 类型,并使用它重构了一些代码以实现最佳性能。最初,Span听起来可能有点复杂,但正如我希望我已经展示的那样,在本示例中使用它非常简单。

谢谢阅读!

如果您想了解有关高性能.NET和C#代码的更多信息,可以在此处[7]查看我的完整博客文章系列。

References

[1] 有关编写高性能C#代码的系列文章: https://www.stevejgordon.co.uk/motivations-for-writing-high-performance-csharp-code
[2] 示例代码,可以在GitHub上找到: https://github.com/stevejgordon/BenchmarkAndSpanExample
[3] Span 结构: https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=netcore-2.2
[4] 有关Span的所有信息:探索新的.NET主体: https://msdn.microsoft.com/en-us/magazine/mt814808.aspx
[5] C#7.2:了解Span: https://channel9.msdn.com/Events/Connect/2017/T125
[6] Span By Adam Sitnik: https://adamsitnik.com/Span/
[7] 在此处: https://www.stevejgordon.co.uk/writing-high-performance-csharp-and-dotnet-code

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

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

相关文章

pycharm配置git拉取项目代码,并添加版本控制

安装Git 打开网页进入git官网&#xff0c;找到git官网下载地址&#xff0c;下载git工具并且安装。 pycharm配置git 点击File -> Settings -> Version Control -> Git 选择Git安装的路径&#xff0c;点击OK 选择一个项目 进入我们需要拉取的项目&#xff0c;点击…

.NET Core开发实战(第22课:异常处理中间件:区分真异常与逻辑异常)--学习笔记(上)...

22 | 异常处理中间件&#xff1a;区分真异常与逻辑异常这一节我们来讲解一下错误处理的最佳实践系统里面异常处理&#xff0c;ASP.NET Core 提供了四种方式1、异常处理页2、异常处理匿名委托方法3、IExceptionFilter4、ExceptionFilterAttribute源码链接&#xff1a;https://gi…

MYSQL开窗函数详解

基本概念 MYSQL8.0支持窗口函数&#xff08;Window Function&#xff09;&#xff0c;也称分析函数。窗口函数与组分聚合函数类似&#xff0c;但是每一行数据都会生成一个结果。如果我们将mysql与pandas中的DataFrame做类比学习的话他们的对应关系如下&#xff1a; SQL分组聚…

你可能需要了解一下的中台

【中台学习】| 作者 / Edison Zhou这是恰童鞋骚年的第201篇原创文章在数字化转型热潮下&#xff0c;各家企业都想建设中台&#xff0c;那么中台是怎么发展起来的&#xff1f;有哪些类型的中台&#xff1f;中台到底是个啥&#xff1f;本文为你一一解答这些问题。1学习背景与前言…

github运行不流畅问题

快速流畅访问Github工具 下载链接如下&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1RwdrNK__Vx-AEuUr8sM6pg 提取码&#xff1a;a0tw –来自百度网盘超级会员V3的分享 下载后解压&#xff0c;双击运行.exe文件 运行后长这样&#xff0c;给它丢一边不管就行了。

聊聊微信的Dark模式

大家好&#xff0c;我是Z哥。这周微信公布了一个我期待已久的好消息。周一的时候对外公布说&#xff0c;已经完成了iOS版本的Dark模式开发&#xff0c;可能会在下一个版本上线。▲截图来源于微博&#xff0c;版权归原作者所有真的是千呼万唤使出来&#xff0c;很多人期待这个功…

chrome浏览器快速访问stackoverflow

原因&#xff1a;国内网非常多的网站都使用免费的 Google CDN 服务来加载某些 js、字体样式库以提升网页浏览体验&#xff0c;例如 jQuery、Google Fonts。但是目前 Google 的大多数网站在大陆无法正常访问&#xff0c;因此这些本身是加快网页载入的库反而成为了阻塞网站加载的…

【开源要闻】Canonical发布新OpenStack工具、Kubernetes访客引导方法

Canonical发布支持CephFS的OpenStack Charms 20.02Canonical近日宣布了OpenStack Charms 20.02的全面上市&#xff0c;这是用于在Ubuntu上设计&#xff0c;构建和管理OpenStack私有云的强大工具的主要版本。OpenStack Charms 20.02是一个令人兴奋的版本&#xff0c;它增加了主要…

开窗函数(1)-部门工资前三员工

已知表 题目 公司的主管们感兴趣的是公司每个部门中谁赚的钱最多。一个部门的 高收入者 是指一个员工的工资在该部门的 不同 工资中 排名前三 。 编写一个SQL查询&#xff0c;找出每个部门中 收入高的员工 。 以 任意顺序 返回结果表。 示例 参考答案 selectbase.Departmen…

Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式

一、前言上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战的文章&#xff0c;其中有不少博友给我提了问题&#xff0c;其中有一个博友问我的一个场景&#xff0c;我给他解答的还不够完美&#xff0c;之后我经过自己的学习查阅并阅读了相关源代码&…

缺失值处理

处理思路 在数据预处理过程中&#xff0c;难免会有数据的确实情况&#xff0c;无论是自己爬虫获取的还是从公开数据源上获取的数据集&#xff0c;都不能保证数据集是完全准确的&#xff0c;难免会有一些缺失值。而以这样数据集为基础进行建模或者数据分析时&#xff0c;缺失值…

在线教育,异军突起,有一种华丽转身,叫做.NET在线讲师!(全职/兼职皆可)...

在线教育2020年&#xff0c;注定是不平凡的一年&#xff0c;疫情来袭&#xff0c;人们只能潜伏在家&#xff0c;很多人因此没有了收入甚至没有了工作&#xff0c;各个行业&#xff0c;也正在快速升级和重新洗牌&#xff0c;这其中&#xff0c;有一个行业&#xff0c;异军突起&a…

python计算相关系数

皮尔逊相关系数 用numpy实现 import numpy as npx np.array([1, 4, 3, 5]) y np.array([1, 3, 4, 5]) pc np.corrcoef(x, y)print(pc)输出结果&#xff1a; 调用scipy.stats中的pearsonr方法 from scipy.stats import pearsonr import numpy as npx np.array([1, 4, 3,…

领域模型

领域模型是对领域内的概念类或现实世界中对象的可视化表示。又称概念模型、领域对象模型、分析对象模型。它专注于分析问题领域本身&#xff0c;发掘重要的业务领域概念&#xff0c;并建立业务领域概念之间的关系。学会了面向对象的思想&#xff0c;却依然写不出好的面向对象的…

重新定义代理的扩展性:WebAssembly在Envoy与Istio中的应用

原文&#xff1a;https://istio.io/blog/2020/wasm-announce/作者&#xff1a;CRAIG BOX, MANDAR JOG, JOHN PLEVYAK, LOUIS RYAN, PIOTR SIKORA (GOOGLE), YUVAL KOHAVI, SCOTT WEISS (SOLO.IO)译者&#xff1a;陆培尔编者按Istio的架构在1.5版本中发生了翻天覆地的变化&#…

2019年第十届蓝桥杯国赛B组试题E-路径计数-dfs(坑题)

【问题描述】 从一个 5 x 5 的方格矩阵的左上角出发&#xff0c;沿着方格的边走&#xff0c;满足以下条件的路线有多少种&#xff1f; 总长度不超过 12&#xff1b; 最后回到左上角&#xff1b; 路线不自交&#xff1b; 不走出 5 x 5 的方格矩阵范围之外。如下图所示&#xff…

.NET Core开发实战(第22课:异常处理中间件:区分真异常与逻辑异常)--学习笔记(下)...

接下来介绍使用代理方法的方式&#xff0c;也就是说把 ErrorController 整段逻辑直接定义在注册的地方&#xff0c;使用一个匿名委托来处理&#xff0c;这里的逻辑与之前的逻辑是相同的app.UseExceptionHandler(errApp > {errApp.Run(async context >{// 在 Features 里面…

【朝夕技术专刊】Core3.1WebApi_Filter-Authorize详解

欢迎大家阅读《朝夕Net社区技术专刊》第6期我们致力于.NetCore的推广和落地&#xff0c;为更好的帮助大家学习&#xff0c;方便分享干货&#xff0c;特创此刊&#xff01;很高兴你能成为忠实读者&#xff0c;文末福利不要错过哦&#xff01;前言&#xff1a;本部分文档将详细给…

以正确的方式下载和配置 ASP.NET Core 官方源码

我们可以在Github上面直接查看ASP.NETCore 3.x的源代码&#xff0c;但是我们也可以把源代码下载下来进行查看。而下载源代码进行查看有很多好处&#xff1a;任意的导航源代码内置了一个示例项目直接调试源代码下载源代码想下载并配置好源码&#xff0c;你需要&#xff1a;最新版…

h5应用 vue 钉钉_uniapp开发一个小视频应用(一)

“uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;是一种终极的跨平台解决方案&#xff0c;这里的平台&#xff0c;主要指的是App平台(android、ios)、小程序平台、H5平台。开发者编写一套代码&#xff0c;可发布到iOS、Android、H5、以及各种小程序(微信/支付宝…