大约两年前,我发表了一篇文章,详细的介绍了 Bing 的中央工作流引擎(XAP)从 .NET Framework 升级到 .NET 5 的过程。你可以通过这篇文章来了解 XAP 的工作原理,以及它在 Bing 全局中的位置。从那时起,XAP 一直是微软许多搜索和工作流相关技术的关键组件,并在新的集成中发挥了核心作用,比如新的 AI 驱动的 Bing。
人们对功能和性能的期望越来越高,这意味着我们对 .NET 作为基础设施的关键部分的依赖性越来越深。
在过去的两年里,我们是 .NET 6 和 .NET 7 的早期采用者,现在正将目光转向 .NET 8。我们发现每个版本都比上一个版本更容易升级。作为一个核心平台团队,我们对每个版本的 .NET 带来的性能提升和新功能都充满了强烈的动力。通过积极地测试并升级到最新版本,并向 .NET 团队提供反馈,对他们的计划产生一定影响。
本文将重点介绍我们所做的一些主要更新和面对的挑战,以及我们在积极跟进最新 .NET 版本时最终取得的成果。
Hybrid No-More
当第一次升级到 .NET 5 时,我们是在一个混合模型中进行的,其中我们仍然是基于 .NET Framework 4.x 构建的,但是加载程序集并在 .NET 5 下运行。我们能够引导自己和我们的内部合作伙伴,在保持构建简单的同时确保一些关键的向后兼容性,但仍然利用新的运行时。
在我们升级到 .NET 6 之前,我们迁移到了一个多目标系统,直接针对 .NET Framework 和 .NET 5 进行构建。通过一些条件编译,我们可以开始采用新的 API 来获得一些性能优势。到目前为止,我们已经完全弃用了 .NET Framework,并且把精力完全集中在新的运行时上。
.NET 6
随着我们完成了从 .NET Framework 的升级,我们预计迁移到 .NET 6 会更容易。然而,仍然有一些小挑战和一些意想之外的收获,等下会详细说明。
在测试期间,我们注意到一些后端 HTTP 调用的问题。事实证明,SocketHttpHandler 的变化使实现更符合规范,但并没有能够灵活地处理产生错误负载的错误服务器。.NET 团队修改了代码,使它兼容性更强。
我们遇到的另一个有趣的运行时问题是一个罕见的旋转计数 bug。它表现为在单个数据中心(可能是因为特定的硬件和流量配置)中,偶尔出现非常高的延迟峰值和较低的整体可用性。实际上,在我们提出这个问题时,. NET 团队已经修复了它。在应用修复之后,我们看到可用性有了明显且直接的提升:
这两个问题都解决后,发布基本上就很简单了,我们只需要比较少的代码更改。最终,整体性能全面提升了约 5%。还有一个方面的改进比这要显著得多:启动时间。
当进程启动时,它会加载几千个程序集。所有这些代码都需要 jitted,最好是在真正的用户查询到达之前。多年来,我们已经迭代了许多技术来实现这一点,但目前的方法是通过分析 JIT 事件日志来查看哪些方法最需要它,并在后续的启动中主动地 jitting 它们。我们尽可能快地在所有处理器内核上执行此操作。
.NET 6 的 JIT 效率因此得到了极大的提升,对启动时间产生了很大的影响:
在一些机器 SKU 上,启动时间提高了近40%!难以置信!
.NET 7
.NET 6 发布后,我们就立即集中精力升级到 .NET 7。有两个主要变化需要特别注意:
1.线程池的操作方式。
2.一个新的基于区域的 GC。
详细的测试表明,新的线程池设计为我们带来了更好的性能,所以这里不用担心。
事实上,新的垃圾收集器设计在几个月的时间里由 .NET 开发者在我们的一些测试机器上进行了具体且广泛的测试,以确保它没有引入任何回归。在一个对运行时如何工作的假设进行了高度优化的系统中,当运行时的一个基本部分极大地改变了其实现时,总数令人担忧。
幸运的是,在测试中我们发现进程花在 GC 上的时间平均提高了24%,虽然一开始并不是很多。在实际生产中,这个比例甚至更高,接近30%。
解决了这两个问题后,我们通常会看到大约10-17%的效率提升,这取决于数据中心和工作负载类型。
很大一部分性能提升来自运行时 CPU 使用率的提升,也就是在 Bing 中查询时 CPU 使用率的降低:
注意:上图中的值并不代表查询延迟,它是所有并行路径上的 CPU 使用率的总和。
在 GC 改进、新的线程池和对处理器更有效地使用之间,我们实现了 P95 延迟3-7%的改进,在更高的百分比上改进更多。这种效率的提升让我们能够给用户更快的服务体验,或者在某些情况下,减少资源使用并以不同的方式实现运行时收益。“效率”对于科技公司来说就像2023年的年度词汇,.NET 7在我们的努力中扮演了很关键的角色。
.NET 8
.NET 8 已经发布了预览版。我们将开始在这个新的运行时下测试工作流引擎。到目前为止,年复一年地升级到最新版本已经被证明是显著提高性能的最经济有效的方法。我们从未期望“免费”获得所有这些性能优势,我们一直追求的是平价。很难想象效率方面比较大的改进会永久持续下去(尽管目前还没有停止的迹象)。我们很高兴能站在 .NET 进步的风口浪尖上!
点我前往原博客~