写点什么

虚拟座谈会:.NET 中的高性能应用

2017 年 8 月 24 日

本文要点

  • .NET 自 4.0 以来得到了大幅的性能提升,很值得重新考虑一下基于旧版本.NET 框架所做的假定。
  • 在讨论性能时垃圾回收是个重复出现的主题,它带来了许多 CLR 和语言的提升,比如引用返回和 ValueTask
  • 在内存分配上更细粒度度量的性能分析 API 会成为对当前版本 API 的最大改进(目前正在开发)。
  • .NET 包含有很多丰富的并行编程 API 和类库,比如 Task Parallel Library、Rx.NET 和 Akka.NET 。这一领域的挑战在于教育用户,以及为使更广泛的社区轻松使用而所做的抽象。
  • .NET 作为一个生态环境,具备支持物联网或小型设备的必要构件:.NET Native、.NET Standard 以及便捷 CLR 组件。

随着.NET Standard 和对.NET 应用开放的新平台的出现,近期大家对.NET 的性能越来越感兴趣了。在传统认知上,.NET 不是一个编写高性能应用的平台,而通常会把它和企业级应用联系在一起。

近几年,有几个正显著改变这一状况的趋势。首先也是最重要的是,.NET Standard 为许多各种不同的平台打开了大门,比如移动电话和物联网设备。这些设备提出了新的挑战,资源的约束会比桌面和服务器多得多。

同时,能够在 Linux 上运行服务端应用也向开发人员展示出极重要的机会和未知的领域。.NET Core 还有待积累运行高性能应用的成功案例。在.NET 生态环境中正在呈现出几个新的趋势:微服务、容器和无服务器。每个都带着他们自己的性能需求。

讨论组成员

  • Ben Watson - 《编写高性能.Net 代码》和《 C# 4.0 How-To》的作者
  • Sasha Goldshtein - Sela Group 的技术总监、微软 C# MVP 和区域主管
  • Matt Warren - 热爱寻找和修复性能问题的 C#开发人员
  • Maoni Stephens - .NET GC 的核心开发人员
  • Aaron Stannard - Petabridge 的创始人和技术总监

InfoQ:以你们的经验来看,在性能方面.NET 处于什么位置?它和其他主流平台相比如何?

Ben Watson: .NET 处于强有力的位置,而且在不断变强。CLR 团队对性能总是非常关注,最近几年提升了.NET 许多方面的性能,比如运行时编译和垃圾回收。在微软,我的产品已经推动了一些这种改变,很高兴看到这些改变能对整个世界有益,而不仅仅是微软那些大型应用。

当然,也有一些弱点。在线上的世界里,每个请求都很重要,像运行时编译和垃圾回收会妨碍取得极致的性能。也有解决办法,但需要很大的工作量,那么就要看这最后一丝性能对你有多么重要了。

性能永无止境,任何平台都是在做权衡。.NET 给了你一些运行时难以置信的特性,但你必须按它的规则去充分利用它。最好的性能需要最好的技术努力。其他平台会有不同的权衡,但无论如何技术努力都是不可或缺的。

Sasha Goldshtein:过去,大家都认为.NET 是针对大型企业和 Web 应用的平台,它们的性能不像游戏、嵌入式系统和实时消息处理那么重要。我认为,最近几年这种看法发生了改变,因为更多的人开始认知到 C#和 F#的便利,以及.NET 框架的强大功能和在非软件操作系统上使用同一代码库的潜在能力,使得投身于使用.NET 开发高性能系统值得去做了。

微软在平台性能上做了相当大的投入。举些例子:几年前已引入.NET Native 以改善启动时间和减少客户端 app 的内存使用;.NET 4.5 和 4.6 对垃圾回收的伸缩性做了重大改进;.NET 4.6 改造了运行时编译器,使其支持向量化了;C# 7 引入了引用局部变量和引用返回,这是一些旨在让语言层有更好的性能的特性。

总而言之,可能对于我个人而言是可以更容易地用 C 或 C++ 这样的低级语言来编写小型高性能应用了。然而,若想把非托管代码引入到已有的系统中,或使用这些语言开发大型数据库,还需要慎重考虑。对于某些特定类型的高性能系统,把宝押在.NET 上还是可行的,只要你意识到相应的风险并具备在代码层解决它们的工具,并且可以应对生产环境中出现的问题就可以了。

Matt Warren: .NET 经常被拿来和 Java 比较,因为他们有非常相似的运行时,也就是说,他们都有运行时编译器、垃圾回收,并支持类似的面向对象的语言(C# v. Java)。我想说的是,这两个运行时有着同样的性能特点,但当然也各有所长。

大家经常说,Java Hotspot Compiler(JIT)性能比.NET JIT 更为优化。此外,它还有一些对性能有所帮助的额外特性,例如,它将在一开始解释一个方法(为改进启动时间),然后可能在稍后再编译为机器码。

然而,在另一方面,现在.NET 已经支持自定义值类型(结构)有很久了,它能让你更好地控制内存层,并有助于减少垃圾回收的影响。Java 在不久的将来可能会有自己版本的值类型,但目前还没有。

我认为,.NET 是一个稳固的平台,它在各个方面都做得不错。它总被打上比某某平台慢的标签,但我觉得对于它适用的负载类型,它的表现还不错。

Maoni Stephens:讨论性能时,往往都会提到垃圾回收。因为我就是从事这方面工作的,所以我在此次座谈会上的回答将更多地围绕垃圾回收的上下文来展开。虽然在有些人的印象中.NET 具有高生产力而性能一般,但是其实已经有很多由.NET 写成的具有高性能需求的产品。为确保我们的垃圾回收能够处理这些需求,我们在交付重大特性之前总会由内部合作伙伴来测试它,让它来处理现实中最具压力的负载,比如必应或 Exchange。我们不只是靠基本和大型基准测试,尽管它们非常有用,但严重缺乏在现实世界中性能的正确表现。.NET 垃圾回收这十几年来已经做过大量的调整,与其他主流平台相比已经非常有竞争力了。

当然,垃圾回收只是框架的一部分。当有些人讨论垃圾回收性能时,其实是在说“垃圾回收是如何来处理我给它的工作负载的”。如果工作负载把许多压力都放到垃圾回收上,自然会比没那么大的压力情况产生更长时间和 / 或更频繁的垃圾回收暂停。从历史角度来看,.NET 框架实现的方式和大家用.NET 编写代码的方式往往会很容易把非常重的压力丢给垃圾回收。虽然我们一直都在改进我们的垃圾回收,但具有高性能需求的人也需要不停去度量和分析什么因素正在影响着他们应用的性能。这和你在使用什么框架或类库无关,换句话说,如果你想要达成和保持高性能,就需要去搞清楚你的工作负载是怎样的表现。如果你对垃圾回收很感兴趣,可以阅读这篇博客,里面仅对垃圾回收的比较做了阐述。

实际上,垃圾回收是向成熟框架提出的一部分挑战,而较新的框架可能不需要面对这些挑战。我们已经有了许多写完并用了很久的类库,通常我们很难用新特性去打破现有的局面。即便我们要实现,哪怕只有一小部分愿意为高性能投入更多精力的用户使用,也要尽可能保证其像框架的主要特性一样可靠。我看到一部分新框架推崇能够测探性能的特性,但这实际是把不切实际的可靠性负担甩给了用户。那不是我们想要采取的方式。

我们的客户总要求我们拿出更好的性能,因为他们这方面正在超越极限!我听到许多过去常常使用.NET 的人说,当他们由于工作变动而不得不切换到其他框架时,其实真的很想念它。所以现在.NET 能运行于更多的操作系统令我非常地兴奋。就像之前所说的,鉴于.NET 类库和应用的编写方式,往往会给垃圾回收产生大量的工作,所以我认为我们需要在可预见的未来时间内不断地改进垃圾回收的性能,以及制作能帮用户度量性能的工具。与此同时,我们也在考虑研发更多能够降低垃圾回收压力的特性。

Aaron Stannard:大多数“性能”差异归根结底在于框架的顶层实现。出于很多原因,JavaScript 不是个“快速”的语言,然而在所有 TechEmpower 基准测试中 Node.JS 都把 ASP.NET 抛在了身后。为什么?主要原因是 ASP.NET 采用的是几十年前特定于 Windows 的老技术,最初的实现采用的是一种同步的设计思路。而另一方面, Node.JS 一开始就设计为异步的了,而且从开始到现在采纳了许多其他性能优先的选择。

即便 ASP.NET 可能是慢的,但 C#语言和 CLR 本身是非常快的。CLR 支持值类型,与 JVM 相比这是性能方面的巨大优势,因为我们可以从本质上释放为这些类型分配的内存(在一定程度上……)。CLR 近几年已经有了很多的改进,使它在运行期“聪明”多了。例如,垃圾回收器已经可以在回收垃圾时对内存进行很好地碎片整理了,这样对后续内存访问的速度提升有显著的帮助。CLR JIT 也可以使用性能分析信息决策如何在运行时最好地编译本地函数,比如是否使用转移表或简化 C#接口上只有少量代码实现的 if 语句。

C#语言本身还具备一个好处,那就是能够让开发人员对编译器工作有更多的控制权,你可以提供关于内联函数的指令,你可以把内存锁定到空闲的堆上和垃圾回收范围之外,如果需要,你甚至可以将 CLR 分配对象视为不安全的,回溯 C 风格的指针运算。由于指针的存在,相比任何其他托管的语言使用 C#和 CLR 进行性能方面的调整都会更加灵活。

InfoQ:你们用.NET 编写高性能应用时所面对的挑战是什么?

Ben Watson:最大的挑战,在某些方面来说仍旧是垃圾回收。当大多数人首次开始用.NET 编程时,会觉得这是理所应当的,不去管它。对于大多数应用来说,它确实无关紧要,除非你的内存分配模式非常糟糕。

然而,垃圾回收问题不应被忽视,特别是在永不停机的高性能系统中。像此类程序的编写与基础应用完全不同。例如,当你默认编码模式导致每秒分配几 GB 的内存分配时,如何调整使你的程序不再是垃圾回收机制的主要诱因呢?如果你每毫秒都计数,它什么时候有时间去回收呢?

不是说你要完全避免垃圾回收,而是说要使它的成本尽可能小,使它不会有太大的影响,在性能分析中不再是最突出的问题。为做到这一点,可能需要非常大量的技术工作,但结果会令人吃惊的。实际上,在托管代码中也有可能达成像在非托管代码中的性能,技巧虽然不同,但办法总是有的。

做这件事需要从根本上转变大家编写托管代码的思维,大家必须对得起工程师的名号。在某种程度上,这种技术思维的转变是最难的部分。我们经常不把自己的工具当回事,当性能真正重要时却无能为力了。

Sasha Goldshtein:就像使用任何托管的平台一样,垃圾回收器是许多.NET 应用变慢或暂停的源头。当堆变大时,达成几十甚至几百 GB,鉴于.NET 没有完全并行的回收器(所以某些收集需要把应用的线程完全挂起),这就与管理感应式垃圾回收有许多不同了。结果,许多人伸手就用非托管的内存、把对象放到缓冲池里却不再释放,或者为了不出现完全的垃圾回收而非常小心地调整系统。这很令人遗憾,在我看来,垃圾回收应为应用开发人员服务,然而有时却本末倒置了。

性能挑战的另一个来源是潜伏在各种 C#语言结构和.NET 类库 API 中的各种地雷。例如,通常很难预测哪个类库方法打算分配内存,或者如何以最小内存分配的方式来使用语言结构。

最后,在 C/C++ 中仍然很容易去做一些操作,比如指针处理、高级的向量(目前 System.Numerics.Vectors 中已不再涵盖)、一般计数代码。在 C#中得到同样的结果经常需要些反常的结构,比如不安全的代码,从模版生成的代码,甚至动态生成的 IL 或汇编代码。

Matt Warren:在我之前工作过的几个项目里,对垃圾回收影响的处理很成问题。持续运行数小时或数日的系统会分配大量的内存,在某一时刻,垃圾回收不得不去彻底清理。因为该应用有着非常严格的隐含需求(低于 40 毫秒),所以这些暂停就成了问题。那么学习如何去检测、度量和缓解这些垃圾回收的影响就成了我们最大的性能挑战。

Maoni Stephens:垃圾回收是由内存的分配和残留引发的。我们想要帮框架程序集的作者和客户找出与此相关的信息,继而使垃圾回收的作业更加简单。这些年,我们与客户探讨过展现内存分配的几种思路。我记得有一次我们探讨了提供“内存分配打分”的 API,但这没那么简单,因为当你采用不同的参数或执行不同的代码路径时,会有截然不同的内存分配。当然,我们已经开发了能对你的内存分配有所帮助的工具(我们有 ETW 事件和以贴切的方式呈现这些事件的工具),但如果能在我开发的时候就让我看到调用一个 API 会分配多少字节的内存,或者从 A 点到 B 点我已经自己分配了多少字节的内存,那就更好了。运行时可以提供这些信息,但我还没看到有 IDE 可以做到这一点。

我们所从事的另一件事是,PerfView 中一个能帮助查看旧一代对象是如何持有新一代中对象的,也就是说,使它们留存下来。这是短垃圾回收成本的重要部分。我想我们还没有将其推向大众(但代码在 PerfView 里了,感兴趣的话可以查看 CROSS_GENERATION_LIVENESS),因为我们还没有时间使此实现足够稳定。它能让你找出可以设置为 null 的对象的域,让垃圾回收在自然发生时知道你已经不再持有它们的生命周期。

Aaron Stannard:总之,我发现基础 CLR 本身在性能工具方面还落后于其他平台。这也是我编写 NBench 的其中一个原因,它是一个针对.NET 应用的性能测试框架。

举个例子,Java 运行环境暴露 Java 管理扩展(JXM)工具链接,这使开发人员可以非常容易地访问到详细的性能统计,通过图形界面或者 JMX API 都可以。许多针对大规模 JVM 应用的管理和监控工具都是直接以此为基础开发的,立刻进入脑海的一个例子就是针对 Apache Cassandra 的 DataStax OpsCenter。退一步说,你至少不能不经度量就去讨论性能是“好”、“坏”或“更好”吧,在我的经验里,在 CLR 上这么做可是个很痛苦的经历。

目前针对.NET 性能的内置解决方案是 Event Tracing for Windows (ETW) 和性能计数器,与 JMX 相比,在一定规模上实现正确性和可靠性还是有点难的。你可能无法在.NET Core 中使用它们,因为它们是特定于 Windows 技术的,据我所知,目前这种监控.NET Core 应用的内置选项还不多。

我用.NET 开发高性能系统遇到的其他大的挑战,是去弄清楚我所依赖的其他.NET 类库和组件的性能。因为大多数开发人员不清楚如何去对他们的代码进行性能测试乃至性能分析,所以在任何时候把第三方类库引入到自己的应用中都要冒着很大的风险。我记得在我其中一个大规模.NET 应用中 (每天数百万个事务) 安装了一个 StatsD 监控组件,这个类库让我的 CPU 利用率飙升了百分之三十左右。结果是,它用底层 UDP 端口做了些很怪的事,我不得不给修复它好让性能回到之前可接受的水平。

InfoQ:.NET 在处理并行和并发上做得怎么样?有重大改进的空间吗?

Ben Watson:Task Parallel Library(TPL:任务并行类库)是该平台的一大飞跃。它完全转变成了.NET 中的并行编程。我们在线上平台中使用它开发了一个处理实时查询的高扩展性应用。若要得到更好的性能,正确编写的 TPL 应用仅通过增加更多的处理器就可以很简单地进行扩展了,实际上我们多次发现,都不需要修改代码。

即便如此,编写高扩展性系统仍然是一个非常难的事情。它需要深入理解硬件、操作系统以及 CLR 的内部构件。你的期望越高,就越需要慎重考虑每一层。

它正朝着更高、更容易开发的方向抽象,但仍未达到初级程序员就可以高效准确使用的程度。

从实用的角度来看,实现完美并发经常与避免潜在的阻塞调用有关。在这个领域,工具还有些改进的空间。我们需要更好的方式来强调代码中有问题的区域,并建议消除这些性能障碍的方式。除非你正在用高度结构化的框架编码,限制你的一样做法,否则很容易就会因为不慎重地使用公共 API 而破坏了性能。如果我们可以把平台和此类工具结合,帮你担起并发的重担,我想这能极大地帮助大众简单有效地使用并发。

Sasha Goldshtein:.NET 框架有丰富的并发并发 API:Task Parallel Library、TPL DataFlow,以及 async/await 语言特性。这些包括数据并行(当你想要并行获取大规模条目并进行特定转换时),以及无结构的任务并行(当你有一组想要安排进一个管道并发执行的任务时)。最后,还包括有一些好的异步 I/O 操作,它们对于高性能服务端代码来说极为重要。

然而,在 GPGPU 上还没有太多的进展,为使.NET 应用可以分流通用计算到 GPU;在混合方面,有像 Xeon Phi 这样的内部处理卡;而在向量方面,有比当前的 System.Numerics.Vectors 更先进的向量指令。使用.NET 编写高并发的计算密集型应用仍受限于主流处理器(受限于插槽和核相对较少),如果你把混合计算引擎添加到系统中,就变成非常难了。

Matt Warren:我们在.NET 中很幸运,里面有许多可能帮到我们的语言和运行时特性。async/await 关键字、Task/Task 乃至经过大量调优的线程池。然而,看起来这样的重大改进现在正在不断进入高层类库,例如实现“Actor”并发模型的 Akka.NET 。

Maoni Stephens:在 V1.0 中我们交付了 Server GC 模式(一种并行 GC)和 Workstation GC 模式(一种非并行但在一定程度上是并发的)。在 V4.0 我们演进了 Workstation GC 使其有更强的并发,然后在 V4.5 中使其可用于 Server GC 了(也就是 Background Server GC),所以它已经同时支持并行并发了,这让我们可以极大改进大规模服务器负载,大内存堆上的完全垃圾回收暂停从 gen2 的数秒阻塞降到了通常 Background gen2 的 10 毫秒。我们一直都在不断地改善已有的垃圾回收,我们还将引入重大改进以帮我们消除一些障碍,比如降低完全阻塞垃圾回收的频率到非常低的程度(使大多数人不再担心这一点),以及在不牺牲太多吞吐率的情况下真正缩短暂停时间。

Aaron Stannard:.NET 有一些并行并发的最佳工具:Task and Parallelism Library (TPL)、 async/await、asynchronous garbage collection、reactive extensions (Rx),以及许多像 Akka.NET 之类的 OSS 类库。

改进空间总是有的:例如,当前为 TPL 添加的 ValueTask 类型,它让一些任务可以返回自由分配的内存,这有非常大的好处。

这让我想起了并行并发里其中的一个重要部分,即从性能角度来看可以改进的一个地方是,教育我们的最终用户如何更好地使用这些工具。关于 async / await 有一个货船崇拜的神话,特别是现在,导致有些开发人员认为 async 是一个用于“高速模式:启动!”的关键字,于是就产生了如下的代码块:

复制代码
await Task.Run(() => {
// 不会从并发得到任何好处的代码
});
await Task.Run(() => {
// 不会从并发得到任何好处的更多代码
});

并发和并行不是魔法,它们是为了提升效率而必须要适当应用的思想。.NET 作为一个生态环境没有强调这些要点的学习,因此我们中那么多人尽管采取了这些神奇的工具,却构建了缓慢和浪费资源的应用。所以换句话说,关于这些高性能和并发技术的文档和言传身教极为重要,然而如今的交流却忽视了它们,实在令人扼腕。

InfoQ:与桌面.NET 框架相比,.NET Core 在性能层面带来了什么优势吗?

Ben Watson:CLR 的基础部分也是对性能影响最大的部分。我觉得.NET Core 最大的优势是“真爱在这里”,注意力都集中在这里了。桌面 .NET 不是事后的想法,但是确切来说,是能更清楚地预计到会有更多人转到.NET Core。如果有缺陷需要修复,或者改进需要实现,那么首先会在.NET Core 了中处理,然后才在相对缓慢的.NET 更新发布时间表中予以考虑。

Sasha Goldshtein:尽管.NET Core 和桌面.NET 都来自于同一个共享的代码库,但是在.NET Core 中有许多创新和优化的机会,因为它有更快的发布节奏。然而,如果你比较.NET Core 和桌面.NET 两个时间点的版本,它们来自于同一个代码库,我不觉得你会认为有性能差异:其内部是相同的 C#编译器、相同的 JIT 和相同的垃圾回收引擎。

Matt Warren:幸运的是微软正好写了一篇名为“.NET Core 中的性能改进”的博客,里面介绍了一些当前的优势。有些改进方法可能曾在.NET 框架中出现过(基于向后兼容的考虑),但它表示当前的.NET Core 是领先的。

看起来,社区的帮助(因为它会开源)加上能够比.NET 框架更加快速地迭代,意味着在.NET Core. 中会实现更多的改进。

我可以想象,往后差异会越来越大,也就是说,越来越多的性能改进将只出现在.NET Core 中,特别是如果他们还新增额外的 API 的话。

Maoni Stephens:所有.NET SKU 共享同一个垃圾回收,垃圾回收 POV 的最大优势在于,你实际上可以在 Core 中快速完成一个特性让大家去试用,因为它是开源的,而不必等到下一个桌面版本才提供给所有的客户。

Aaron Stannard:巨大成功。已实现了对 Spans、System.IO.Pipelines 和许多基础.NET 类型(比如字符串)工作方式的改进,这是针对这些类目前在传统.NET 4.* 框架上工作方式的所有重大改进。还实现了像 ConcurrentQueue 这些工作方式的根本性改进,这应该有助于显著提升并发应用的速度。

InfoQ:在高性能领域,.NET Native 扮演着什么角色呢?在什么情况下它能胜过托管的副本?

Ben Watson:.NET Native 有许多潜力。通常在移动设备场景和 Windows 商店应用中它非常有用,在那儿你可不想使用用户有限的电池寿命进行运行时编译。然而,我很想看到此类技术传播到传统桌面和服务端应用,每台机器一次又一次地在运行时编译同一个东西会造成大量电力、故障时间、延迟时间等方面的浪费,尤其是数据中心更为突出。NGEN 有严苛的限制,.NET Native 最终可以解决的。

Sasha Goldshtein:.NET Native 是一个很有意思的项目,因为它把完全的静态提前编译 (AOT (ahead-of-time)compilation) 带进了.NET 的世界。我说“完全的 AOT”,是因为 NGen 从一开始就已经是.NET 框架的一部分了,但它仍然需要完整安装.NET 框架,需要用于元数据的原始解释语言模型,以及可以根据需要开启运行时编译。

不过最重要的是,.NET Native 构建所做的优化步骤比基本运行时编译或 NGen 要先进得多(实际上,使用了像 C++ 一样的后端优化)。这并不奇怪,因为运行时编译是在非常严格的时间约束下运转的,然而.NET Native 编译会为一个复杂项目花几分钟。最终结果很让人震惊:对于计算密集型应用,应用更好的优化某些部分能大幅提速;而通过减少需要从磁盘加载的依赖和类库,以及使用运行时编译,能提升启动时间。

Matt Warren:.NET Native 的其中一个好处是你的代码是提前编译的,而不是在运行时由 JIT 编译器编译的。还有两个好处,一个是因为在运行时编译的场景中,所以启动时间比较快,但首次被调用的方法必须予以编译,这会带来开销。第二个好处是,静态提前编译器可以做更加昂贵且大范围的优化,而这在 JIT 中是不可能的。JIT 编译器无法进行这种尝试,因为它必须在限定的时间里进行工作,而 AOT 编译器则不受此约束。

Maoni Stephens:.NET Native 减少了启动时间和磁盘占用。因为它使用了一个做整体分析的编译器,它能够执行更多的优化。我认为在容器的应用场景中它会成为重要角色。

Aaron Stannard:从部署的角度来看,.NET Native 会比较方便,因为它让用户可以部署.NET 应用到从未安装过.NET 运行环境的机器上,这有点儿像静态编译过的 C++ 应用。但我觉得这么编译出来的应用在性能上比不上它的托管副本。本机编译的应用不能充分利用 JIT 的运行时性能分析,它本来可以让应用基于实际在运行时的使用情况自己做出性能调整的。.NET Native 应用应该比总是需要.NET JIT 的应有更短的启动时间,但我觉得即时编译的应用的运行时性能通常会更好一些。

InfoQ:最近.NET 语言里添加了几个性能相关的特性,比如值元组和引用返回。你们觉得接下来会看到什么特性?

Ben Watson:我很想看到在高吞吐率场景下缓冲对象以降低内存分配率这一方面的进展。我知道许多缓冲内部数据结构以降低内存加载的.NET API,即便一些官方设计指南很有帮助,但这种缓冲技术应用于非常特殊的用途。一些 CLR 恰恰可帮你轻松创建简单可复用的对象,减少垃圾回收上的开销。

我还希望看到更具伸缩性的 JIT。它不慢,但是,在多核上即时编译代码会有瓶颈,它使你无法在运行大型应用时把初始化时间降到最低。

Sasha Goldshtein:我希望 C#能够在数字类型上,或者不共享接口的其他任意家族类型上使用真正通用的代码。我想到了 C++ 模版,因为你可以创作一个对任意数字类型(或家族类型)有作用的函数模版,而不必预先规定限制条件。不幸的是,看起来引入此类特性需要在 CLR 方面做出改变,而涉及语言设计是个非常谨慎的事,自 CLR 2.0 引入泛型以来就在尽量避免变动。

尽管我可以理解这些版本,但我仍然希望看到另一个特性,那就是在 C#编程中内联解释语言甚至是内联汇编。这会为进一步优化、向量指令和其他硬件相关的优化打开新的大门。

Matt Warren:Span,虽然我知道它们即将到来,但其实可能只是个说辞!能够高效访问接近或匹配数组的“任意内存的连续区域”是最大的好处。一旦他们是在运行时实现的,我们就会看到 Span 广泛应用于所有框架和第三方类库,这将发挥非常广泛的作用。

Maoni Stephens:我们在垃圾回收上的哲学已经极少有旋钮了,因为我们认为用户不应该为了调优内存而去花很多时间精通垃圾回收的知识,这应该是我们的工作。我们只为用户提供了几个配置,告诉我们他们的应用是什么类型就可以了,比如,是服务端还是客户端。我们一直都在遵循这个哲学。我们想提供更多的配置,让客户告诉我们发应用性能上下文中如何帮助他们,例如,他们的应用是否想要用更大的堆换得更可预测的暂停;或者在机器上有 64GB 的内存,但是他们希望某一进程只使用 32GB。我们正在增加能对糟糕局面做出更好反应的新机制和策略,以使垃圾回收更为健壮,那么我们就可以满足这些配置的要求了。这是垃圾回收团队在不久的将来将交付的下一个与性能相关的其中一个特性。

Aaron Stannard:我希望看到一些基本的集合能够原生支持引用返回,从性能角度来看,这样能带来一些真正有趣的可能性,使之能够传递引用到相同的值类型或一系列这种类型的迭代器。我不知道可能会有什么性能收益,但我希望可以进行实验得出结论。

不过我希望看到的最大特性是,用于.NET Core 的跨平台的性能工具。我也需要能够得到在 Linux 运行环境中垃圾回收以及其他 CLR 核心部分的运行时性能指标!

InfoQ:虽然高性能通常与服务端应用有关,但移动和物联网设备之类的其他平台也提出了独特的性能需求。.NET 可以满足这些非传统平台的需求吗?

Ben Watson:我持乐观态度,我们将把.NET 看作为一个用于小型和更多独特设备的优秀平台。我们正看到.NET 更多的模块化方法,在此各种组件可以按需增增减减,或者替换。随着日趋成熟,我觉得我们将看到一些用于不同设备的非常适当的架构。

在许多方面,我认为今后的高性能应用(.NET 及其他)在于适当的创建,以及具有约束的可复用框架,通过这些约束实现高性能模式。

Sasha Goldshtein:移动和物联网方面,它们非常关注于快速启动时间、更少的内存占用,以及面对垃圾回收的决定性性能。这个领域还有些工作要做,比如.NET Native,而在减少垃圾回收暂停或削减.NET 应用内存大小上普遍没有太大的进展。我现在担心,如果它偶尔来次 5 秒种的垃圾回收,那就很难用 C#做物联网应用了,而高性能的移动应用也存在同样的问题。未来在.NET Core 和 CoreCLR 上性能方面的工作必须考虑到这些非传统性需求。

Matt Warren:要使.NET 运行于其他平台(比如树莓派和三星的 Tizen OS)还有许多工作要做,迄今为止看起来.NET 运行时调整得不错。针对更多受限的设备做出改变多么有意思呀(比如降低内存开销),所以我们全是受益者!

Maoni Stephens:机理通常都已经有了。我们需要做些性能调优使它们可以更好地应用于其他的平台。举个简单的例子,当电话开始使用 Core 时,我们需要针对 Workstation GC 减小段的大小。UWP 应用往往会使用更多的垃圾回收句柄,这意味着我们可能需要使我们的垃圾回收句柄代码路径更加优化。这些事需要我们去收集数据,它对于我们决定优先级非常重要。

Aaron Stannard:在我们公司,已经有客户将.NET 用于高吞吐率的物联网应用了。现在甚至都用不着.NET Core。我期待,随着.NET Core 运行时的成熟和更多的类库开始支持它,会发展得越来越好。在移动这一方面,随着 Unity3D 迁移至新版本的 C#和 Mono 运行时(顺便一提,它也快得多了,从我们的基准测试结果来看,它已经接近 Windows 桌面 CLR 的水平),我期待看到 Unity3D 应用的性能有显著提升。

总结

自.NET4.0 以下,.NET 及其语言在性能方面已经有了显著的发展,使古老的假设成了过去式。除了垃圾回收、JIT 和 BCL 方面的改进,.NET Native 之类的新增内容和 TPL 还拓宽了可用.NET 处理的应用场景。许多已列入计划或正在实现的改进证明,它们一直在密切关注升级和新的基准测试。

关于作者

Ben Watson 从 2008 年以来已经是微软的一名软件工程师了。必应是世界领先的、基于.NET 的、高性能服务端应用之一,它通过数以万计的机器为无数客户处理着大量低延迟请求,他作为必应平台的核心开发人员在该平台的建设中起着不可或缺的作用。他是性能的狂热追求者,花了很多的时间在教育团队高性能.NET 的最佳实践上。在他闲暇的时间里,喜欢享受地理寻宝、乐高、阅读、古典音乐,陪伴妻子和孩子在美丽的太平洋西北部度过美好时光。他是《编写高性能.Net 代码》和《 C# 4.0 How-To》的作者。

Sasha Goldshtein 是 Sela Group 的技术总监、微软 C# MVP 和区域主管、Pluralsight 的作者,以及国际咨询师和培训师。他是《Introducing Windows 7 for Developers》(微软出版社,2009 年)和《Pro .NET Performance》(Apress 出版社,2012 年)的作者,他是一名多产的博客写手和开源贡献者,开发了许多著名的培训课程,其中包括.NET 调试、.NET 性能、安卓应用开发以及现代 C++。他的咨询工作主要围绕分布式架构、产品调试和性能诊断,以及移动应用开发。

Matt Warren是一名 C#开发人员,他最喜欢的就是发现和修复性能问题。他与 Azure、 ASP.NET MVC 和 WinForms 合作过一些项目,比如用于存储政府气象数据的网站、医疗监控设备以及一个确保啤酒桶没漏的检测系统!他是一名 BenchmarkDotNet 和 CoreCLR 的开源贡献者。Matt 目前主要忙着在 CA Technologies 做 C#产品分析工具和在 www.mattwarren.org 写博客

Maoni Stephens 是.NET GC 的主要开发人员。她在闲暇时喜欢阅读垃圾回收方面的论文和观看日本动漫或动物世界。平时,她喜欢使她关注的事情更加高效。

Aaron Stannard Petabridge 的创始人和技术总监,这是一个.NET 数据平台公司。Aaron 是 Akka.NET 最初的贡献者之一,他编写了 Akka.Remote module 和 Helios (底层 socket 中间件)。在从事 Petabridge 的事业之前,Petabridge Aaron 创立了 MarkedUp Analytics,一个针对应用开发人员的实时应用分析和营销自动化公司,他们的底层借助了 Cassandra 和 Akka.NET

查看英文原文: Virtual Panel: High Performance Application in .NET

2017 年 8 月 24 日 17:311740

评论

发布
暂无评论
发现更多内容

你是否真的懂数组?

架构师修行之路

数组 数据结构与算法

Github+docsify零成本轻松打造在线文档网站

Java全栈封神

Java GitHub 全栈 文档 docsigy

支持 100 种语言的 Canva 是怎么做本地化管理的?

葛仲君

产品经理 本地化 产品本地化 国际化

微服务架构

不在调上

获奖名单公布 | 写作平台八月宠粉福利来袭,参与创作领取限时大奖~

InfoQ写作平台官方

写作平台 征稿 活动专区

贵州:“区块链+”促经济转型产业升级

CECBC区块链专委会

libuv 异步网络编程之 TCP helloworld

Huayra

网络编程 libuv

十五张图带你彻底搞懂从URL到页面展示发生的故事

执鸢者

前端 浏览器 页面展示

【权限系统设计】ACL, DAC, MAC, RBAC, ABAC模型的不同应用场景

小隐乐乐

DT时代释放金融数据价值,驱动金融商业裂变

华为云开发者社区

金融科技 华为云 modelarts 数据价值 用户细分

计算机网络基础(十七)---传输层-TCP的可靠传输

书旅

TCP 计算机网络 网络协议 计算机基础 TCP/IP

AndroidStudio解决Unknown host 'd29vzk4ow07wi7.cloudfront.net'. You may need to adjust the proxy settings in Gradle

小菜鸟学php

丢弃掉那些BeanUtils工具类吧,MapStruct真香!!!

Hollis

Java 程序员 后端

华为阿里下班时间曝光:所有的光鲜,都有加班的味道

程序员生活志

华为 加班 阿里

9个常用ES6特性归纳(一般用这些就够了)

华为云开发者社区

Java 程序员 编程语言 ES6 编程效率

2.2.2 类反射场景与使用 -《SSM深入解析与项目实战》

谙忆

5招详解linux之openEuler /centos7防火墙基本使用指南

华为云开发者社区

centos7 网络安全 防火墙 openEuler 网络环境

英特尔推出“OpenVINO领航者联盟”,携手DFRobot推进AI商业落地新探

飞天鱼2017

为什么 90 后一言不合就跳槽?

非著名程序员

程序员 个人成长 职业成长 延迟满足感 即时反馈

LeetCode题解:21. 合并两个有序链表,迭代(优化),JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

腾讯SaaS千帆对渠道的态度——合作共赢的诚意

人称T客

原创 | 使用JPA实现DDD持久化- O:对象的世界(3/3)

编程道与术

Java hibernate DDD JDBC jpa

联盟链落地与激励机制

CECBC区块链专委会

当我们谈注册中心时谈什么?

小楼

zookeeper nacos 注册中心

week 10

Geek_2e7dd7

week 10

Geek_2e7dd7

论做AI芯片的正确姿势

flow

十二张图搞懂浏览器安全——(同源策略、XSS、CSRF、跨域、HTTPS、安全沙箱等知识点)

执鸢者

https 浏览器安全 同源策略 XSS 跨域

最右JS2Flutter框架——动画、小游戏的实现(四)

刘剑

flutter 前端 探索与实践

Linux神器strace的使用方法及实践

华为云开发者社区

Linux 运维 工具 后端 Strace

微服务的认识

chenzt

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

虚拟座谈会:.NET中的高性能应用-InfoQ