dotnet 使用 Crossgen2 对 DLL 进行 ReadyToRun 提升启动性能

我对几个应用进行严格的启动性能评估,对比了在 .NET Framework 和 dotnet 6 下的应用启动性能,非常符合预期的可以看到,在用户的设备上,经过了 NGen 之后的 .NET Framework 可以提供非常优越的启动性能,再加上 .NET Framework 本身就是属于系统组件的部分,很少存在冷启动的时候,大部分的 DLL 都在系统里预热。启动性能方面,依然是 .NET Framework 比 dotnet 6 快非常多。而在破坏了 .NET Framework 的运行时框架层的 NGen 之后,可以发现 .NET Framework 的启动性能就比不过 dotnet 6 的启动性能。为了在 dotnet 6 下追平和 .NET Framework 的启动性能差异,引入与 NGen 的同等级的 ReadyToRun 用来提升整体的性能。本文将告诉大家如何在 dotnet 6 的应用里面,使用 Crossgen2 工具,给 DLL 生成 AOT 数据,提升应用启动性能

我预计本文是具有时效的,各个概念都在变更,本文是在 2022.05 编写的。如果你阅读本文的时间距离本文编写时间过长,那请小心本文过期的知识误导

开始之前,还请理清一下概念

在 dotnet 里面,这些概念都在变来变去,还没有完全定下来。在聊 dotnet 里面的 AOT 之前,是必须先来做一个辟谣的。第一个谣言是 AOT 意味着性能更高?其实不然,采用 AOT 能减少应用启动过程中,从 IL 转换为本机代码的损耗,但通过分层编译(TieredCompilation)技术,这部分的差异不会特别特别大,再加上 dotnet 6 引入 的 QuickJit 技术,还能进一步缩小差距。但即使这么说,启动性能方面,采用 AOT 还是很有优势的,因为启动过程是性能敏感的,再加上大型项目在启动过程中将需要执行大量的代码逻辑,即使 JIT 再快和加上动态 PGO 的辅助下,依然由于需要工作的量太多而在性能上不如采用 AOT 的方式。由于 AOT 是生产静态逻辑,只取平台最小集,而无法和 JIT 一样,根据所运行设备进行动态优化,这就是为什么运行过程中的性能,在 JIT 进入 Tier 2 优化之后的性能要远远超过 AOT 的方式。换句话说,全程都使用 AOT 而不加入任何 JIT 只是提升启动性能,但是降低了运行过程的性能

那如果我启动性能也要,运行过程的性能也要呢?这个就是 ReadyToRun 技术的概念了,在 DLL 的进入调用时,先采用 AOT 技术,将部分逻辑预先跑了 JIT 且将跑了之后的二进制逻辑也记录到 DLL 里面。如此可以实现在首次调用方法时,减少 JIT 的戏份,尽可能使用之前 AOT 的内容,从而提升应用启动性能。而在应用跑起来之后,依然跑的是 JIT 的优化,如此即可兼顾启动性能和运行过程的性能

如何实现 ReadyToRun 这个概念?就需要用到几项技术和工具,其中 Crossgen2 就是进行 ReadyToRun 的工具。通过 Crossgen2 工具,可以对 DLL 进行静态 AOT 编入 DLL 内

但是如此做法也不是没有缺点的,那就是额外编入 DLL 的 AOT 的内容,将会增大 DLL 的体积。而 DLL 体积的增大将会降低启动过程中读取文件的性能,再加上 AOT 和 JIT 过程的切换也是需要判断逻辑,加上了这部分损耗之后,再对比一下 QuickJit 技术,实际上采用 Crossgen2 进行 ReadyToRun 不是对所有的 DLL 都能提升启动性能

为了解决以上问题,在 dotnet 里再引入了 PGO 的概念。启动过程里面调用的方法是有限的,如果可以了解到应用启动过程将会调用哪些方法,只是将这部分方法进行 AOT 那么对 DLL 体积的影响将会小非常多。这就是 PGO 需要解决的问题,通过引入 PGO 这个概念,在应用运行过程里面,了解应用启动过程将会碰到哪些 IL 逻辑,将这部分逻辑记录下来,用于指导 ReadyToRun 过程进行 AOT 哪些方法。从而让 AOT 过程不需要针对所有的 IL 逻辑,而是仅对应用启动过程需要用到的才进行 AOT 过程。如此即可更大的提升应用的启动性能。不过 PGO 可以做的事情可不只是 ReadyToRun 的指导,还可以作为 JIT 过程中,让 JIT 了解可以预先在后台线程里面跑哪些 IL 转换从而达到更高的启动性能。必须说明的是,我询问了几位大佬了解到,当前的 PGO 还是一个玩具,虽然性能评测上可以达到很好的效果,然而还没有具备发布环境使用的能力

对于 AOT 不可反编译的辟谣。如上文可以看到 ReadyToRun 技术上,依然是保留 IL 逻辑,只是在 DLL 里面再加入 AOT 生成的二进制数据,从而减少启动过程的 JIT 的损耗。也就是说如果采用 ReadyToRun 的技术,可以让应用有更快(不一定是更快)的启动性能,同时也拥有原本的运行过程的性能。但是否可以做到不可反编译,自然是做不到的,原本的 IL 代码依然还在,也就是说采用 ReadyToRun 技术,没有任何额外的保护能力。那第二个问题,如果采用纯 AOT 技术,能否达到代码保护能力?嗯,能加一点点。如果配合上混淆的话,感觉上是差不多了。如果要说防破解能力的话,两个的打分,一个是 60 分,一个是 70 分,满分是 100 分。真要别人看不懂,代码写垃圾些就好了,我全力发挥的时候,保证连自己都看不懂

回到主题,如何在 dotnet 里面通过 Crossgen2 工具进行 ReadyToRun 提升应用性能?千万别被官方骗了,如果只是在 csproj 上或者是在发布的时候加上 ReadyToRun 的命令参数,恭喜你,是真的用了 Corssgen2 工具。但优化呢?只是优化了入口程序集而已

真的想要有比较大的优化,是需要将除了入口程序集之外的其他程序集也通过 Crossgen2 工具进行 ReadyToRun 才可以有比较大的提升的。例如我的一个大型应用,在启动过程里面将 WPF 框架里面大概十分之一的模块都碰了一次,使用 JitInfo.GetCompiledMethodCount 了解到,在第一个窗口 Show 出来之前就有 5 万个方法调用。这个应用的入口程序集占比太小了,如果使用官方的方法,只是对入口程序集进行 ReadyToRun 那么性能上还真被 .NET Framework 完虐

为了让 dotnet 6 应用的启动性能能媲美 .NET Framework 应用的启动性能,可以采用 ReadyToRun 对标 .NET Framework 的 NGen 技术。以下将告诉大家如何使用 Crossgen2 工具对 DLL 进行 ReadyToRun 提升启动性能

默认的 Crossgen2 工具是采用 NuGet 分发的 DotnetPlatform 类型的 NuGet 包,里面包含了独立发布的 Crossgen2 工具。换句话说,可以在 %localappdata%\..\..\.nuget\packages\microsoft.netcore.app.crossgen2.win-x64 找到此工具。如果没有找到的话,那试试用一句 dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true 命令让 dotnet 为了构建 ReadyToRun 而帮你将 Crossgen2 下载

以上的 Crossgen2 工具放在 microsoft.netcore.app.crossgen2.win-x64 文件夹里面,这里的 win-x64 指的不是 Crossgen2 工具的能力,不是说这个文件夹的工具只能构建出 win-x64 的。而是说这个工具本身是 win-x64 的。这个工具是能构建出其他的平台的 AOT 的。换句话说是在 Windows 的 32 位系统里面,将会拉的工具是 microsoft.netcore.app.crossgen2.win-x86 的包

进入版本号文件夹,再进入 Tools 文件夹即可找到 Crossgen2.exe 可执行文件,这就是工具本文。例如在我的设备上的工具路径是

C:\Users\lindexi\.nuget\packages\microsoft.netcore.app.crossgen2.win-x64\6.0.5\tools\Crossgen2.exe

接下来将告诉大家如何使用这个工具

这个工具的使用需要传入的参数推荐是一个 rsp 文件,大概的命令行调用如下

C:\Users\lindexi\.nuget\packages\microsoft.netcore.app.crossgen2.win-x64\6.0.5\tools\Crossgen2.exe "@C:\lindexi\Fxx\F1.rsp"

具体的参数都放在 rsp 文件里面,大概内容如下

--targetos:windows--targetarch:x86--pdb-O-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-console-l1-1-0.dll"-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-console-l1-2-0.dll"-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-datetime-l1-1-0.dll"-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-debug-l1-1-0.dll"-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-errorhandling-l1-1-0.dll"-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-fibers-l1-1-0.dll"-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-file-l1-1-0.dll"-r:"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.5\api-ms-win-core-file-l1-2-0.dll"--out:"C:\Users\linde\AppData\Local\Temp\Crossgen2\Crossgen2\KokicakawheeyeeWhemhedawfelawnemhel.dll"C:\lindexi\Code\empty\KokicakawheeyeeWhemhedawfelawnemhel\KokicakawheeyeeWhemhedawfelawnemhel\bin\release\net6.0-windows\win-x86\publish\KokicakawheeyeeWhemhedawfelawnemhel.dll

大概由以下几个部分组成。每一行都是一个独立的参数,分别内容如下

  • --targetos:windows: 准备执行的系统平台。进行 ReadyToRun 将生成 AOT 代码,这是平台强相关的,必须说明是哪个平台

  • --targetarch:x86:准备生成的对应平台,是 x86 还是 x64 等

  • --pdb: 这是可选的,表示要生成 PDB 符号文件。如不加上这一句将不生成 PDB 文件。生成的 PDB 文件是 ni.pdb 文件,配合原本的 DLL 的 PDB 文件即可方便进行调试

  • -O: 这是可选的,表示需要进行优化。相当于 Release 版本。推荐默认都加上,否则将几乎没有优化效果,或者说只有反向优化效果

  • -r:"xxx.dll": 这里将会重复很多行,一行一个程序集文件的本地路径。让工具了解到有哪些引用可以去找到。工具在准备 AOT 过程,需要找到所引用的程序集。这些参数就是告诉工具对应的程序集放在哪。可以多加入很多程序集,因为只是给工具使用的参考引用,工具会根据自己的需求,去找到对应的程序集文件。如果工具发现传入的有多余的,那将会自动忽略多余的。推荐将整个 dotnet runtime 都加入,但是要注意加入的版本必须是和发布的版本是一致的,否则启动过程如果炸了,那就凉凉。如果应用是独立发布的,那就列出应用独立发布文件夹里面的所有 DLL 文件,不需要加上额外的运行时文件夹

  • --out:"xx.dll": 处理之后的输出文件路径

  • xxxxx.dll 输入程序集的路径

构建出 rsp 文件,作为参数,调用 Crossgen2 工具,即可完成对程序集的 ReadyToRun 过程。多个程序集就多次重复以上过程即可

必须画重点的是,调用 Crossgen2 工具进行 ReadyToRun 是不一定能提升启动性能的,这是一个需要测量的过程。每个 DLL 在调用了 Crossgen2 工具进行 ReadyToRun 是会修改文件体积的,整个变更也是会影响启动性能的。推荐在优化应用启动性能,进行足够的测量,方法如下

使用 Crossgen2 工具对每个 DLL 来一次,包括框架层的 DLL 也来一次。然后逐个 DLL 替换,测量应用启动性能。如果发现某些 DLL 进行了 ReadyToRun 反而降低启动性能,或者某些 DLL 加大的文件体积对比启动性能的优化来说不划算,那就不对这些 DLL 进行优化

以下是测试的对 dotnet runtime 底层和 WPF 框架的 DLL 进行 ReadyToRun 优化之后,对 walterlv 大佬的某个应用的启动性能的影响,值得一提的是对于不同的应用,测试的数据将会存在很大的出入,核心原因在于不同的应用启动过程将访问的模块有所不同

02ee4f4e593262a63a289e86b3033955.png

这个数据是没有多少参考价值的,因为对于不同的应用来说,以上的结果将会有变化。如果你想要采用 ReadyToRun 技术提升应用启动性能,还请必须测量每个 DLL 在经过 ReadyToRun 对启动性能的影响。如果你的时间充裕的话,还可以测量对多个 DLL 优化的组合对启动性能的影响

我所在团队的某个大型应用,在经过了 ReadyToRun 技术的优化,启动性能提升百分之三十

但也必须说明的是,不是所有的应用使用 ReadyToRun 都能有优化启动性能,例如我的一个小应用,只要采用了 ReadyToRun 技术,启动性能基本上都是降低了。总的来说,采用 ReadyToRun 技术是需要进行性能测量的

参考文档

WPF dotnet 使用本机映像 native 优化 dotnet framework 二进制文件

WPF 通过 ReadyToRun 提升性能

Conversation about crossgen2 - .NET Blog

runtime/crossgen2-compilation-structure-enhancements.md at main · dotnet/runtime

runtime/Program.cs at main · dotnet/runtime

编译配置设置 - .NET Microsoft Docs

ReadyToRun deployment overview - .NET Microsoft Docs

利用 PGO 提升 .NET 程序性能 - hez2010 - 博客园

JitInfo.GetCompiledMethodCount(Boolean) Method (System.Runtime) Microsoft Docs

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

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

相关文章

使用myeclipse建立maven项目(重要)

maven是管理项目的,myeclipse是编写代码的。第一次写项目都要配置好多东西,很麻烦,now 来看看怎样新建一个maven项目。 工具/原料 myeclipsemaven方法/步骤 因为教程使用的maven是自己下载配置的,并没有使用myeclipse自带的&#…

LeetCode 每日一题 Day 22 || 枚举(数学方法)/二分

1954. 收集足够苹果的最小花园周长 给你一个用无限二维网格表示的花园,每一个 整数坐标处都有一棵苹果树。整数坐标 (i, j) 处的苹果树有 |i| |j| 个苹果。 你将会买下正中心坐标是 (0, 0) 的一块 正方形土地 ,且每条边都与两条坐标轴之一平行。 给你…

不用@微信官网了,用python给自己的微信头像加个小国旗

国旗LOGO(png透明格式): 微信头像 合成结果: import base64 import os import re from io import BytesIO from PIL import Image import tkinter as tk from tkinter import filedialog# 水印图片 可以自己指定 #markImageImage…

C语言试题106之有一对兔子问题

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月 后每个月又…

Blazor University (34)表单 —— 获得表单状态

原文链接&#xff1a;https://blazor-university.com/forms/accessing-form-state/获得表单状态源代码[1]有时&#xff0c;我们需要获得 <EditForm> 子内容中的表单状态。最常见的用途是当我们需要访问输入的 CSS 类时&#xff0c;指示输入是否被修改或有效/无效。例如&a…

[转]c# 中间件 的扩展模型(.net webapi/.net Core 的 MiddleWare 处理模型)

在学习 asp.net WebApi 或者asp.net Core 的时候&#xff0c;它们管道的处理模型跟 asp.net MVC/WebForm 的管道模型是不一样的。 asp.net WebApi 或者asp.net Core 他们使用了一种叫做“中间件”的处理模型&#xff0c;相对于传统管道模型&#xff0c;剔除了很多非必要的处理…

【ArcGIS Engine二次开发】入门基础(1):ArcGIS Engine简介及开发环境搭建

文章目录ArcGIS Engine概述ArcGIS Engine与ArcObjects的关系ArcGIS Engine下载及安装ArcGIS Engine概述 ArcGIS Engine简介 ArcGIS Engine是ESRI公司在2004年推出的用于开发C/S架构GIS应用软件的工具包&#xff0c;是将用于构建ArcGIS整套产品的组件库——ArcObjects的比分功…

微软Visual Studio 2019版本16.3 正式发布,支持 .NET Core 3.0

微软正式发布了Visual Studio 2019 16.3版本&#xff0c;主要更新内容如下&#xff1a; .NET Core 3.0 Visual Studio版本16.3包括对 .NET Core 3.0 的支持。 注意&#xff1a;如果使用的是.NET Core 3.0&#xff0c;则需要使用Visual Studio 16.3或更高版本。 .NET Core桌…

C语言试题120之输入两个正整数 m 和 n,求其最大公约数和最小公倍数

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:输入两个正整数 m 和 n,求其最大公约数和最小公倍数 分析:利用辗除法 2 、温馨提示…

OSChina 周一乱弹 ——致我们终将逝去的青春

2019独角兽企业重金招聘Python工程师标准>>> 我们的青春是这样的。 从幼儿园午睡开始&#xff0c; 做了一万遍的广播体操&#xff0c; 一条充满了“血”和“泪”的三八线 遍地开花的煎饼果子摊 五毛钱只能养活三天的小鸡 象征着财富和地位的弹珠 放学后 奔向世界 放…

【ArcGIS Engine二次开发】入门基础(2):ArcGIS开发方式(VBA、DLL、Add-in、Engine)对比

文章目录2.1 使用VBA进行桌面软件开发2.1.1 VBA开发方式2.1.1 VBA代码的安全性2.2 使用DLL进行桌面软件开发2.2.1 DLL开发方式2.2.2 DLL功能的应用部署2.3 使用Add-in进行桌面软件开发2.4 使用ArcGIS Engine构建独立应用程序对于桌面GIS应用来说&#xff0c;ArcGIS平台不仅提供…

使用.Net驱动Jetson Nano的OLED显示屏

背景最近几年&#xff0c;边缘计算的需求急剧爆发。人工智能、物联网和 5G 的演进给边缘计算带来了无限的可能性。因为工作原因&#xff0c;有幸在2019开始接触了英伟达 Jetson 家族的各种边缘计算设备&#xff1a;Nano&#xff0c;TX2&#xff0c;AGX&#xff0c;NX等&#xf…

工程测量不用愁,120个视频动画帮你忙,简单易学直观明了!

工程测量不用愁,120个视频动画帮你忙,简单易学直观明了!文末提供下载地址。

2016-2017-2学期《程序设计与数据结构》教学进程

2016-2017-2学期《程序设计与数据结构》教学进程 目录 考核方式课前准备教学进程 第00周学习任务和要求第01周学习任务和要求第02周学习任务和要求第03周学习任务和要求第04周学习任务和要求第05周学习任务和要求第06周学习任务和要求第07周学习任务和要求第08周学习任务和要求…

C语言试题121之输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。 分析:利用 w…

5.4 Spring AOP

2019独角兽企业重金招聘Python工程师标准>>> 5.4.1 从代理机制初探AOP 来看一个简单的例子&#xff0c;当需要在执行某些方法时留下日志信息&#xff0c;可能会这样写&#xff1a; import java.util.logging.*; public class HelloSpeaker{ pirvate Logger loggerL…

WPF 实现心电图曲线绘制

本文经原作者授权以原创方式二次分享&#xff0c;欢迎转载、分享。原文作者&#xff1a;流浪g原文地址&#xff1a;https://www.cnblogs.com/cong2312/p/16411637.html一、前言项目中之前涉及到胎儿心率图曲线的绘制&#xff0c;最近项目中还需要添加心电曲线和血样曲线的绘制功…

C语言试题110之利用条件运算符的嵌套来完成此题:学习成绩大于等于90分的同学用 A 表示,60到89 分之间的用 B 表示, 60 分以下的用 C 表示。

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:C语言试题110之利用条件运算符的嵌套来完成此题:学习成绩大于等于90分的同学用 A 表…

如何让 Dapper 支持 DateOnly 类型

前言在上次的文章中&#xff0c;我们让 EF Core 6 支持了 DateOnly 类型。那么&#xff0c;Dapper 是否支持 DateOnly 类型呢&#xff1f;public class User {public int Id { get; set; }public string Name { get; set; }public DateOnly Birthday { get; set; } }using (var…

VB常用内部函数大全一览表(建议收藏)

VB提供了大量的内部函数供用户在编程时调用。内部函数按其功能分为数学运算函数、字符串函数、转换函数、日期与时间函数、判断函数和格式输出函数等。 文章目录 算术函数字符串函数日期和时间函数数据类型转换函数算术函数 字符串函数 日期和时间函数