.NET8 上的 Bing :动态PGO的影响

自从我上次更新大家有关.NET在Bing技术栈中的状态以来已经过去了一年多,尤其是位于核心位置的高性能工作流执行引擎。在这段时间里,这个引擎的应用范围只增不减,特别是随着Microsoft Copilot的发布。虽然我们的工作流引擎起源于Bing,但现在可以说它支撑了许多Microsoft应用程序中搜索和数据栈的相当大一部分。

我们从.NET 8的早期预览版开始进行测试。尽管在.NET 8的核心库中有明显的性能递增好处,但促使我们升级的最大因素是对动态PGO(Profile Guided Optimization,配置文件指导的优化)的显著改进。这一功能自.NET 6起就以预览形式存在,在.NET 8中,改进足够显著,以至于默认启用了这一功能。

之前的帖子:

迁移Bing工作流引擎到 .NET5

.NET 5 升级到 .NET 7,再次为必应带来性能提升

动态PGO

鉴于我们的规模,有时会有一些功能开箱即用就能在几乎所有应用中表现良好,但我们仍会给予额外的考虑。

在进程启动时,这个服务器会加载数千个合作伙伴组件,这些组件包含了我们执行工作流程所需的插件。这大约有2GB的代码,其中很多都需要即时编译(JIT)。当第一个用户查询请求击中服务器时,它需要能够在几百毫秒内提供答案,同时避免因即时编译而导致的暂停。

人们自然会想到,像NGEN和Ready2Run这样的技术是否会有所帮助。我们尝试过预编译代码,并取得了不同程度的成功。最终,我们发现最佳的性能与启动时间最小化的平衡是让JIT自行处理,结合在以往运行中检测到的特别关键的方法列表进行预编译。我们在启动时并行加载其他数据时做这件事。我们还在接受真实用户流量之前,通过系统推送一些测试查询。

动态PGO通过根据需要重新编译某些代码来提高运行时代码的质量。理论上,这可以帮助我们改善延迟,但我们需要彻底测试它对启动和第一个用户查询的影响。关于动态PGO的工作原理,你可以在其他地方阅读到,但简而言之:

动态PGO会在新即时编译的代码中加入一些轻量级指令,以记录性能特征并建立一个重新编译候选队列。可能的优化包括:

  • 内联
  • 方法去虚拟化
  • 循环优化
  • 尾递归移除
  • 优化内存中的代码布局以优化处理器缓存
  • ......还有更多

在测试中,我们看到了两个主要结果:

  1. 稳定状态下性能显著提升。
  2. 我们最大的工作负载在第一个用户查询时有一个小的延迟峰值,这表明要么有些方法根本没有编译,要么是因为分析的影响太大。这使得总延迟超出了最高限制。对于我们较小的工作负载来说,这不是问题。 下面的延迟图显示与基线相比,延迟有一个大幅度的峰值:

(请注意,本文档中的图表已删除了特定的内部指标信息,但形状的变化应能让您大致了解相对变化情况。)

在深入调查和来自 .NET 团队的帮助后,我们发现在我们遇到第一个用户查询之前,重新编译(re-jit)队列的大小已经增长到超过300,000个方法!正如我所说,我们的代码量非常大。最终,当进程运行几个小时后,我们通常会有超过两百万个方法。

作为响应,我们实施了一些小的改动:

  1. 增加了额外的预热查询,给更多方法机会进行即时编译(jit)和重新编译(re-jit)。
  2. 在接受用户流量之前稍作暂停,以便让队列有时间排空(我们起初将其静态配置,但有一个事件可以让您监控 JIT 队列大小)。
  3. 一些针对我们超大场景的自定义 JIT 设置:
sh 复制代码
REM 启用 64 位计数器以稍微减少错误共享
set DOTNET_JitCollect64BitCounts=1

REM 移除启动分层编译的延迟
REM (目标是减少总体上执行带有检测代码的方法所花费的时间)
set DOTNET_TC_CallCountingDelayMs=0

有了这些变化,延迟峰值消失了,现在我们可以享受稳定状态下的性能改进。

性能提升

我们所见到的在多个性能特征上的改进,或许是自从从.NET Framework迁移到.NET 5以来最显著的一次。

我们执行一个查询所消耗的CPU周期数减少了13%。换句话说,我们的效率提高了13%,或者再换一种方式表达:这意味着我们需要购买的机器数量减少了13%,以应对不断增长的需求。下面的图表显示了执行工作流程时总CPU时间的下降。

一个图表显示了查询执行中CPU时间的减少。

受到gen0或gen1垃圾回收影响的查询比例下降了20%(我们几乎从不会有查询受到gen2 GC的影响,因为A. 我们的内存管理策略避免了这一点,B. 我们采取了额外的措施以确保机器在gen2 GC迫在眉睫时不会服务用户------这是极其罕见的)。

这个图表显示了GC对查询影响的相对差异:

图表显示了受垃圾回收影响的查询百分比的下降。许多其它指标也显示出查询执行各个阶段有类似上述图表的改进。一些内部延迟降低了超过25%。查询服务的很大一部分是等待其他后端,因此总体查询服务时间的改进幅度稍微小一些,大约8%------但这仍然非常显著!

总结

总的来说,这次的 .NET 发布对我们来说既稳固又相对容易。我们在延迟上取得了改进,在效率上也有了巨大提升,这将在未来几年为我们节省数百万美元。尽管在我们庞大的代码库和严格的延迟要求下,动态 PGO 需要一些微调,但在运行时性能方面,它确实是一个巨大的胜利。

现在,我需要开始为 .NET 9 做准备了......希望在明年能再次向你们报告!

原文信息

作者:Ben Watson Principal Software Engineer, Bing Platform

链接:devblogs.microsoft.com/dotnet/bing...

相关推荐
ai小鬼头2 小时前
Ollama+OpenWeb最新版0.42+0.3.35一键安装教程,轻松搞定AI模型部署
后端·架构·github
萧曵 丶3 小时前
Rust 所有权系统:深入浅出指南
开发语言·后端·rust
老任与码3 小时前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
华子w9089258594 小时前
基于 SpringBoot+VueJS 的农产品研究报告管理系统设计与实现
vue.js·spring boot·后端
星辰离彬4 小时前
Java 与 MySQL 性能优化:Java应用中MySQL慢SQL诊断与优化实战
java·后端·sql·mysql·性能优化
GetcharZp6 小时前
彻底告别数据焦虑!这款开源神器 RustDesk,让你自建一个比向日葵、ToDesk 更安全的远程桌面
后端·rust
jack_yin7 小时前
Telegram DeepSeek Bot 管理平台 发布啦!
后端
小码编匠7 小时前
C# 上位机开发怎么学?给自动化工程师的建议
后端·c#·.net
库森学长7 小时前
面试官:发生OOM后,JVM还能运行吗?
jvm·后端·面试
转转技术团队7 小时前
二奢仓店的静默打印代理实现
java·后端