原文来自互联网,由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权,请联系小编,小编将在24小时内删除。
作者介绍:
史蒂夫·戈登(Steve Gordon)是Microsoft MVP,Pluralsight的作者,布莱顿(英国西南部城市)的高级开发人员和社区负责人。
对于有追求的.NET开发者来说,如何编写高性能的C#代码一直是非常渴求的目标。作为一位.NET方向的MVP,Steve Gordon围绕这个方向,专门写了一个系列共7篇博客,主要探讨探讨支持编写更多性能,低分配代码的现代C#和.NET / .NET Core技术和功能。
译者注:本系列博客的发表时间为2019年2月18日,部分内容可能与新框架代码有些许不同,请自行验证。
本系列的文章包括:
1.编写高性能C#代码的动机2.使用Benchmark.NET对C# 代码进行基准测试的简介3.使用Span 优化代码的简介4.使用String.Create创建没有分配开销的字符串5.使用JetBrains dotMemory对.NET Core内存流量进行性能分析6.解释JetBrains dotMemory中的.NET Core内存时间线7.析JetBrains dotMemory中的大对象堆
第一部分:编写高性能C#代码的动机
这篇文章标志着我期望一系列与性能相关的文章的开始。我将在目录中使用该帖子,以访问这些将来的帖子,并通过讨论我的个人动机以学习更多有关编写高性能C#代码的个人动机来开始本系列文章。
我不确定100%该系列将带我去哪里,但我想使用System.IO.Pipelines,.NET Core 3.0中的新JSON解析器以及Span 和Memory 任何相关的主题。
为什么要编写高性能代码?
我一直对MVC框架和.NET Core之类的内部原理感兴趣。通过了解Microsoft团队如何设计和构建框架,我感到自己可以提高自己的编码能力,并且可以更好地使用该产品。自.NET Core和ASP.NET Core 1.0推出以来,性能一直是团队关注的重点。在过去一年左右的时间里,得到了更大的重视,因此,为了支持高性能方案,引入了新的类型和运行时功能。我看过.NET社区中一些关于性能的著名人物,他们发表了有关性能的文章,因此我决定现在也该深入了解了。
我在Twitter上关注了许多来自Microsoft和.NET社区的优秀人士,并每天获取鼓舞人心的内容。看到其他使ASP.NET Core更快,更高效并在其工作中使用新语言和框架功能的开发人员的帖子非常鼓舞人心。作为一名自学成才的开发人员,我经常觉得有必要学习更多有关我理解的知识。许多与性能相关的新功能对我来说还是个谜。我从概念上理解它们的存在,并且它们正在改进软件,但是我常常不确定为什么以及更重要的是它们如何做到这一点。
我不懂的东西 我喜欢学习,因此我花了一些时间阅读博客文章中的性能主题,观看视频以及参加会议上的演讲。该列表实在太大而无法在此处列出,但是对我来说,一些关键灵感是:
•《Writing High-Performance .NET Code[1]》(编写高性能.NET代码[2])由本·沃森编写。•《Pro .NET Memory Management: For Better Code, Performance, and Scalability[3]》by Konrad Kokosa•Blogs[4] and talks[5] by Adam Sitnik•来自亚当Sitnik的一些博客[6]和演讲[7]。•Marc Gravell的博客[8]•由.NET社区的许多成员,包括本·亚当斯和David Fowler发表的Tweets•Stephen Toub写在Microsoft博客
这并非详尽的清单。我已经从广泛的社区中获取了很多精彩的帖子和信息。我非常感谢我们有这么多内容!
什么是性能?
当我在本系列中谈论高性能时,我指的是两个主要概念。首先是使代码运行更快,从而使操作花费的时间更少。在Web应用程序的上下文中,这可能导致更快的页面加载时间或更快的API响应。对于处理一些传入数据以生成输出的服务工作者风格的应用程序,这与处理的总体吞吐量有关。我认为第二个重要因素是减少内存使用和分配。在我看来,这两个概念融合为“事半功倍”。
在数据处理工作流中,我将性能视为日常工作重点的一个典型示例。我花了很多时间来开发功能,该功能通常通过AWS队列来获取一些数据,并根据该消息执行一些工作。随着时间的推移,我们的许多服务都在增长和发展,如今正在处理大量数据。一个正在工作的示例就是一个队列处理器,它每天处理大约17-20百万条消息。阅读消息后,工作流将处理数据,对其进行验证,对其进行充实并对其进行整形以准备存储到S3和ElasticSearch集群。这个过程并不是很复杂,但是由于数量的原因,我们有一段时间必须水平扩展容器实例以确保我们继续实现所需的吞吐量。
能否提高效率,减少处理时间和内存消耗?我绝对确定可以。
性能的这两个支柱通常是内在联系在一起的,并且影响一个,可以影响另一个。例如,减少代码中的内存分配可以减少GC负载并减少由收集过程引起的暂停,从而可以提高总体速度。
代码过早优化?这也是个问题
当在我完成本系列的过程中,这可能会成为许多人争论的焦点,因此我有必要对这个问题进行讨论。我是否过早地进行了优化,并在复杂的、以性能为中心的代码中花费了太多的时间?
在我的一些示例中可能就是这种情况。我意识到,我正在使用的步骤通常会花费较长的时间编写代码,在某些情况下可读性较低,因此如果将来需要更改代码,则会增加负担。我试图在实验中突破极限,以找到划界的正确位置。
我不太喜欢过早优化这个术语,因为在讨论中它有时会很快被抛弃,并可能导致性能完全被忽略。我坚信性能应该成为我们或多或少编写的所有代码的功能。确定它的重要性以及关注的级别应该是故事计划中的前期讨论。
我认为,团队应该了解他们正在构建的每个功能的需求。他们应该检查预期的使用情况和预期的长期增长,以便适当地讨论可能影响性能的领域。
有时,这可能意味着不需要采取任何特定的措施,而在其他时候,它可能会确定应在早期考虑的巨大的将来扩展需求,因此应尽早采取措施以避免将来完全重写。
当我在这里谈论性能时,它可能很简单,例如确保通过Entity Framework进行的查询使用AsNoTracking支持来减少开销,或者它可能意味着对字节数据进行自定义解析以避免分配。哪个级别合适的范围应该通过预先的早期讨论来确定。
我并不是在提倡每个人都立即花费数周的时间来重写现有代码,以使用Span 和许多其他闪亮的.NET Core功能。我要提倡的是对现有工具的了解,这些工具可以编写快速,低分配的代码,以便就何时使用这些代码提供决策。如果您有代码或服务难以跟上,请安排一些时间来检查问题。
对应用程序进行基准测试和性能分析,以了解有关问题所在和适当情况的更多信息,然后开始使用一些新工具来改进代码并稳定服务。
我举了一个我维护的队列处理器的示例,该处理器处理数百万个事件。两年半以前,当它第一次用.NET Core 1.0编写时,我们做出了一些当时有效的选择。随着其用途的增加以及我们更加全面地了解需求,现在我们计划对其进行审查。
我们当前扩展实例以处理负载的解决方案效果很好,但是我敢肯定,如果我们减少频繁扩展的需求,那么我们可以省钱。作为一家企业,我们需要在变更成本与扩展补偿成本之间取得平衡。
对于新的队列处理器,我们可以从这一经验中学习,并预先考虑它们的扩展需求。
新服务会迅速发展吗?它需要处理越来越多的事件或数据吗?我们能以不同的方式做事,为未来的服务树立一个更好的先例吗?我坚信我们可以做到,而且我已经花了一些时间在这些领域构建新想法的原型。
我无论是在工作还是在家里我都会集中时间关注这件目标,并不断的发现这些功能的局限性,了解什么时候使用它们是合适的,同时我也希望希更多的读者也参与进来。
摘要
尽管我没有涵盖本文中的任何特定内容,但我希望这篇文章能够解释为什么我很高兴了解更多有关性能功能的信息。在接下来的几周和几个月中,我将分享帖子,以分享我对所调查的每件事的经验。
谢谢阅读!如果您想了解有关高性能.NET和C#代码的更多信息,可以在此处[9]查看我的完整博客文章系列。