PowerShell 脚本的作用是通过调用 NGEN (Native Image Generator) 工具来优化 .NET 程序的启动性能。原理、每个步骤的功能以及如何加速 .NET 程序的启动:脚本的目标是对当前运行的 .NET 程序集执行 NGEN 操作,提前生成本地代码图像。在下次启动程序时,系统可以直接使用 NGEN 生成的本地映像,从而避免 JIT 编译,提高启动速度。

$Env:PATH = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
[AppDomain]::CurrentDomain.GetAssemblies() | % {
  $pt = $_.Location
  if (! $pt) {continue}
  if ($cn++) {''}
  $na = Split-Path -Leaf $pt
  Write-Host -ForegroundColor Yellow "NGENing $na"
  ngen install $pt
}

 

PowerShell 脚本的作用是通过调用 NGEN (Native Image Generator) 工具来优化 .NET 程序的启动性能。下面我将详细解释其原理、每个步骤的功能以及如何加速 .NET 程序的启动:

原理:

  1. NGEN (Native Image Generator)
    NGEN 是 .NET Framework 提供的一个工具,它允许将程序集(通常是 MSIL,即中间语言代码)预编译成特定于目标机器架构的本地代码。这意味着在程序运行时,不再需要 JIT 编译,从而减少启动时间和提高运行效率。使用 NGEN 工具生成的本地映像存储在特定的缓存中(例如 %windir%\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files 目录下)。

  2. PowerShell 脚本作用

    • 脚本的目标是对当前运行的 .NET 程序集执行 NGEN 操作,提前生成本地代码图像。
    • 这样,在下次启动程序时,系统可以直接使用 NGEN 生成的本地映像,从而避免 JIT 编译,提高启动速度。

逐行分析:

powershellCopy Code
$Env:PATH = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
  • 解释:这一行将 PATH 环境变量更新为当前 .NET Runtime 目录的位置。[Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory() 获取当前 .NET 运行时的目录路径,并将其设置为 PATH 变量的值。
  • 作用:确保可以找到 ngen 命令和其他必要的 .NET 工具。
powershellCopy Code
[AppDomain]::CurrentDomain.GetAssemblies() | % {
  $pt = $_. Location
  if (! $pt) {continue}
  if ($cn++) {''}
  $na = Split-Path -Leaf $pt
  Write-Host -ForegroundColor Yellow "NGENing $na"
  ngen install $pt
}
  • 解释
    • [AppDomain]::CurrentDomain.GetAssemblies() 获取当前应用程序域中所有加载的程序集(Assemblies)。这些程序集是当前运行的 .NET 应用程序的一部分。
    • 对每个程序集,脚本检查其路径($pt = $_.Location)。如果路径不存在,则跳过该程序集。
    • Split-Path -Leaf $pt 获取程序集的文件名(去掉路径),并将其保存在 $na 变量中。
    • 然后,通过 Write-Host 输出当前正在进行 NGEN 操作的程序集文件名。
    • 最后,通过 ngen install $pt 对每个程序集执行 NGEN 操作,生成本地代码映像。

结果:

  • 执行该脚本后,所有加载的程序集都将通过 NGEN 工具进行预编译,生成本地映像。
  • 当程序下一次启动时,Windows 可以直接加载这些本地映像,而不需要再进行 JIT 编译,从而减少了启动时间,提升了性能。

使用场景:

  • 加速 .NET 应用程序启动时间:通过提前编译程序集,可以显著减少 .NET 应用程序首次启动时的等待时间,尤其是当应用程序较大或者依赖于多个程序集时。
  • 服务器和生产环境:对于长期运行的服务器或高性能要求的环境,提前生成本地代码映像可以提高系统的响应速度和运行效率。

注意事项:

  1. 仅适用于 .NET Framework:NGEN 是 .NET Framework 的工具,而不是 .NET Core 或 .NET 5+ 的工具。对于 .NET Core 和 .NET 5+ 以上版本,启动优化主要依赖于其他机制(如 ReadyToRun 或自包含部署)。
  2. 必须为管理员权限运行:由于 ngen 操作涉及到系统目录中的文件修改,运行脚本时需要具有管理员权限。
  3. NGEN 生成的映像与系统架构相关:NGEN 生成的本地代码映像是特定于架构的,因此如果系统架构发生变化(如从 x86 切换到 x64),可能需要重新生成这些映像。

这个脚本是一种常见的优化 .NET 应用程序启动性能的方法,特别适合需要频繁启动和运行的 .NET 应用程序。

除了使用 NGEN(Native Image Generator)以外,还有一些其他方法可以提高 .NET 应用程序的性能,尤其是在启动速度和运行效率方面。以下是几种常见的替代方法或补充方案:

1. ReadyToRun (R2R)

适用环境:.NET Core 和 .NET 5 及以上版本。

简介
ReadyToRun 是一个类似于 NGEN 的预编译技术,但它专门为 .NET Core 和 .NET 5 及以上版本设计。与 NGEN 不同,ReadyToRun 生成的本地映像更加轻量,并且支持跨平台的应用程序部署。

优点

  • 跨平台支持:ReadyToRun 适用于 Linux、macOS 和 Windows,支持 .NET Core 和 .NET 5 以上版本的应用程序。
  • 更快速的启动时间:通过预编译部分或全部程序集,应用程序的启动时间得到了显著提升。
  • 无需依赖于 .NET 运行时:ReadyToRun 生成的本地映像可以独立于目标机器上的 .NET 环境,减少了运行时的开销。

使用方法

  • 在构建应用程序时使用 dotnet publish 命令的 --self-contained 选项,并启用 ReadyToRun 编译:
    bashCopy Code
    dotnet publish -c Release -r win-x64 --self-contained true /p:PublishReadyToRun=true

2. AOT(Ahead-of-Time)编译

适用环境:.NET 6+、.NET MAUI、Xamarin 和 Blazor 等。

简介
AOT 编译指的是在应用程序发布时就进行完整的代码编译,而不是在运行时使用 JIT(即时编译)。AOT 编译能够生成平台特定的本地代码,从而减少应用启动时的编译开销。

优点

  • 更低的启动时间:由于应用程序已经提前编译成本地代码,启动时无需等待 JIT 编译过程。
  • 适用于无依赖环境:AOT 编译的应用可以更容易地部署到没有完整 .NET 运行时环境的设备上。
  • 跨平台部署:尤其在使用 .NET 6+ 或 .NET MAUI 时,AOT 编译支持多平台(Windows、Linux、macOS)。

使用方法

  • AOT 编译通常在构建或发布时启用,使用 dotnet publish 命令:

    bashCopy Code
    dotnet publish -c Release -r win-x64 --self-contained true /p:PublishAot=true
  • 适用于 Xamarin.NET MAUI 等场景,也可以通过 mono(在某些情况下)实现 AOT 编译。

3. Self-contained Deployment(自包含部署)

适用环境:.NET Core 和 .NET 5+。

简介
自包含部署指的是将应用程序及其所有依赖项(包括 .NET 运行时)一起打包,从而让应用在目标机器上独立运行,不依赖于已经安装的 .NET 运行时。通过这种方式,可以确保应用始终在一个已知的、经过优化的运行时环境中运行。

优点

  • 部署和分发方便:所有必需的文件都包含在应用程序中,用户无需事先安装 .NET。
  • 避免运行时问题:由于应用程序和运行时被捆绑在一起,避免了依赖不兼容的 .NET 版本的问题。
  • 性能优化:通过构建特定平台的自包含部署包,能够减少 JIT 编译的开销并优化启动时间。

使用方法

  • 在发布时选择自包含选项:
    bashCopy Code
    dotnet publish -c Release -r win-x64 --self-contained true
  • PS C:\Users\Administrator> dotnet publish -c Release -r win-x64 --self-contained true

    欢迎使用 .NET 9.0!
    ---------------------
    SDK 版本: 9.0.101

    遥测
    ---------
    .NET 工具会收集用法数据,帮助我们改善你的体验。它由 Microsoft 收集并与社区共享。你可通过使用喜欢的 shell 将 DOTNET_CLI_TELEMETRY_OPTOUT 环境变量设置为 "1" 或 "true" 来选择退出遥测。

    阅读有关 .NET CLI 工具遥测的更多信息: https://aka.ms/dotnet-cli-telemetry

    ----------------
    已安装 ASP.NET Core HTTPS 开发证书。
    若要信任该证书,请运行 "dotnet dev-certs https --trust"
    了解 HTTPS: https://aka.ms/dotnet-https

    ----------------
    编写第一个应用: https://aka.ms/dotnet-hello-world
    了解新增功能: https://aka.ms/dotnet-whats-new
    浏览文档: https://aka.ms/dotnet-docs
    报告问题并在 GitHub 上查找来源: https://github.com/dotnet/core
    使用 "dotnet --help" 查看可用命令或访问: https://aka.ms/dotnet-cli
    --------------------------------------------------------------------------------------
    MSBUILD : error MSB1003: 请指定项目或解决方案文件。当前工作目录中未包含项目或解决方案文件。

4. Trimming(代码修剪)

适用环境:.NET 5+、.NET 6+。

简介
修剪(Trimming)是一个优化技术,用于在发布时移除不必要的程序集和代码,从而减小应用程序的大小并提高加载速度。这可以通过 .NET 6+ 的内置修剪功能来实现,它自动分析应用程序中未被使用的代码并将其剔除。

优点

  • 减少应用程序大小:去除无用的代码和程序集,减小部署包的体积。
  • 提高启动速度:修剪后,程序加载时只需要加载和运行必要的部分,从而加速启动过程。

使用方法

  • 在项目文件中启用修剪选项:

    xmlCopy Code
    <PropertyGroup>
      <PublishTrimmed>true</PublishTrimmed>
    </PropertyGroup>
  • 然后使用 dotnet publish 发布应用程序:

    bashCopy Code
    dotnet publish -c Release -r win-x64 --self-contained true

5. Pre-compiling ASP.NET Core Applications

适用环境ASP.NET Core。

简介
ASP.NET Core 应用中,可以通过预编译 Razor 页面和控制器来加速请求处理时间。通常,ASP.NET Core 会在运行时动态编译 Razor 页面,但是预编译可以将其转换为 .NET 类,从而加速请求处理。

优点

  • 提高请求处理性能:通过预编译 Razor 页面,减少了请求时编译的开销。
  • 更好的可维护性:预编译后的代码可以在发布时进行优化。

使用方法

  • 可以通过在项目文件中启用 MvcRazorCompileOnPublish 来启用预编译:
    xmlCopy Code
    <PropertyGroup>
      <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
    </PropertyGroup>

6. Docker 镜像优化

适用环境:适用于通过 Docker 容器部署的 .NET 应用程序。

简介
如果你在容器环境中运行 .NET 应用程序,可以通过优化 Docker 镜像来提高性能。例如,使用多阶段构建来减少镜像大小,并确保只包含运行时和所需依赖项。

优点

  • 更小的容器镜像:通过多阶段构建减少镜像大小,减少容器启动时的开销。
  • 提高资源利用率:优化后的镜像加载更快,启动容器时可以提高性能。

使用方法

  • 在 Dockerfile 中使用多阶段构建:
    dockerfileCopy Code
    FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
    WORKDIR /app
    EXPOSE 80
    
    FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
    WORKDIR /src
    COPY ["MyApp/MyApp.csproj", "MyApp/"]
    RUN dotnet restore "MyApp/MyApp.csproj"
    COPY . .
    WORKDIR "/src/MyApp"
    RUN dotnet build "MyApp.csproj" -c Release -o /app/build
    
    FROM build AS publish
    RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish
    
    FROM base AS final
    WORKDIR /app
    COPY --from=publish /app/publish .
    ENTRYPOINT ["dotnet", "MyApp.dll"]

 

这些方法都可以在不同的场景下提高 .NET 应用程序的性能和启动速度。选择合适的优化方案需要根据你使用的 .NET 版本、部署方式以及具体应用场景来决定。例如,NGEN 适用于传统的 .NET Framework,而 ReadyToRun、AOT 和修剪则更适用于 .NET Core 和 .NET 5+ 版本。

什么是 NGEN (Native Image Generator)?

NGEN(Native Image Generator)是微软 .NET 框架中的一个工具,用于将 .NET 程序的中间语言(IL,Intermediate Language)编译为本地机器代码(即本地映像)。这样做的目的是提高应用程序的启动性能,因为原本需要在运行时由 .NET CLR(公共语言运行时)进行 JIT(即时编译)的过程会提前完成,从而节省了启动时的编译时间。

NGEN 的工作原理:

  1. 编译过程:

    • 在 .NET 中,应用程序的代码首先被编译成中间语言(IL),这种格式是平台无关的。IL 代码在运行时通过 CLR 转换成本地机器代码,这个过程称为即时编译(JIT)。
    • NGEN 工具提供了一种方法,将 IL 代码直接转换成本地代码(称为“本地映像”)。这个转换过程发生在应用程序部署之前,而不是在每次运行时进行。
  2. 生成本地映像:

    • 使用 NGEN 工具,您可以将应用程序的程序集(.exe 或 .dll 文件)转换成本地代码,并存储在磁盘上,通常是在 GAC(全局程序集缓存)中。
    • 这样,运行时不需要再进行 JIT 编译,直接加载已经生成的本地代码,通常可以显著加快应用程序的启动速度。
  3. 执行本地映像:

    • 当应用程序运行时,CLR 会检查是否存在适合该应用程序的本地映像。如果有,CLR 会直接加载并执行本地映像。
    • 如果没有 NGEN 生成的本地映像,CLR 会执行 JIT 编译,生成并执行本地代码。

NGEN 的使用场景:

  • 提高应用程序启动性能:

    • 对于大型应用程序,JIT 编译可能导致显著的启动延迟。通过使用 NGEN 工具提前生成本地映像,可以减少每次启动时的编译开销。
  • 改进服务器性能:

    • 在服务器应用程序中,长时间运行的应用通常会在启动时经历多次 JIT 编译,NGEN 可以帮助减少这些运行时的开销,确保应用程序高效运行。
  • 部署 .NET 程序:

    • 在生产环境中,通常会先使用 NGEN 生成本地映像,再部署到服务器上,以便确保应用程序启动更快。

NGEN 命令行工具的使用:

NGEN 是一个命令行工具,可以通过命令提示符或 PowerShell 使用。下面是一些常见的 NGEN 使用方法:

1. 编译程序集为本地映像:

bashCopy Code
ngen install <程序集路径>

这个命令将指定的程序集(通常是 .exe 或 .dll 文件)编译成本地映像,并将其安装到 GAC(全局程序集缓存)中。

示例:

bashCopy Code
ngen install C:\Path\To\YourApp.exe

2. 更新已安装的本地映像:

bashCopy Code
ngen update

此命令将扫描计算机上所有已安装的程序集,更新它们的本地映像。

3. 列出已安装的本地映像:

bashCopy Code
ngen display

这个命令显示当前已安装的所有本地映像列表。

4. 卸载本地映像:

bashCopy Code
ngen uninstall <程序集路径>

这个命令会卸载指定程序集的本地映像,释放相关的磁盘空间。

5. 编译所有程序集:

bashCopy Code
ngen executequeueditems

这个命令会执行所有排队等待编译的程序集,通常是在安装时产生的。

NGEN 的优缺点:

优点:

  1. 提升启动速度:

    • 通过将 IL 代码提前转换为本地机器代码,应用程序可以减少运行时的 JIT 编译,显著提升启动速度。
  2. 优化服务器性能:

    • 对于长时间运行的服务器应用,NGEN 可以减少应用程序在每次运行时的编译延迟,提供更好的性能表现。
  3. 减少 JIT 编译负担:

    • 通过在安装时生成本地映像,可以减少运行时对 CPU 和内存的 JIT 编译压力,尤其是对于大型应用程序和复杂的程序集。

缺点:

  1. 增加磁盘空间使用:

    • NGEN 会为每个应用程序生成本地映像,这些本地映像通常存储在 GAC 中,因此会占用额外的磁盘空间。
  2. 需要额外的维护:

    • 每当应用程序的程序集发生更改时(例如,更新或修补),就需要重新生成并安装新的本地映像。
  3. 可能增加复杂性:

    • 使用 NGEN 会使部署过程稍微复杂一些,需要确保本地映像与程序的 IL 代码保持同步。

NGEN 与 ReadyToRun(R2R)比较:

在 .NET Core 和 .NET 5+ 中,微软引入了另一种技术,叫做 ReadyToRun(R2R),与 NGEN 类似。R2R 是一种编译技术,它将程序集预编译为本地代码,但它具有跨平台支持,并且与 .NET Core 的优化更紧密。

与 NGEN 相比,R2R 主要用于 .NET Core 和 .NET 5+ 的应用程序,它是更现代的编译方式,支持跨平台工作(Windows、Linux 和 macOS)。

总结:

NGEN 是一种优化 .NET 应用程序性能的工具,它通过将中间语言(IL)提前编译为本地代码,减少了运行时的 JIT 编译负担,提升了应用程序的启动性能。尽管它具有一些优点,如加快启动速度和提高服务器应用性能,但也有一些缺点,例如需要额外的磁盘空间和额外的维护成本。


NGENReadyToRun (R2R)Self-contained Deployment (自包含部署)AOT (Ahead-of-Time) 编译 的对比表格。每个技术的优缺点以及应用场景会有所不同,理解它们之间的差异有助于在不同的 .NET 项目中选择合适的优化方案。

特性 NGEN (Native Image Generator) ReadyToRun (R2R) Self-contained Deployment (自包含部署) AOT (Ahead-of-Time) 编译
目标平台 Windows (特定于 .NET Framework 和 .NET Core) .NET Core 和 .NET 5+(跨平台) 跨平台(Windows, Linux, macOS) 跨平台(Windows, Linux, macOS)
编译方式 将 .NET 程序的中间语言 (IL) 编译为本地机器代码 将 IL 代码提前编译为本地代码,减少运行时 JIT 编译的负担 将应用程序和所有依赖项一起打包成一个包含所有运行时的单独文件 直接将 IL 代码编译为目标平台的本地机器代码,不依赖 JIT 编译
生成位置 生成的本地映像安装到全局程序集缓存 (GAC) 或指定目录中 生成的本地代码与程序集一起部署 编译时将应用程序及其依赖项和运行时一起打包,部署时无需依赖安装的 .NET 环境 生成本地代码并将其与应用程序一起部署,通常在应用程序启动前完成编译
依赖关系 依赖 .NET Framework 或 .NET Core 运行时,必须在目标机器上安装 依赖于 .NET Core 或 .NET 运行时,通常与应用程序一起打包 不依赖目标机器上的 .NET 运行时,运行时已经包含在部署包中 不依赖于目标机器上的 .NET 运行时,已经包含运行时依赖项
启动速度 提高应用程序启动速度,因为本地映像已经预编译 提高应用程序启动速度,减少运行时 JIT 编译的时间 启动速度较快,因为运行时和应用程序都已经准备好,无需额外的安装步骤 启动速度最快,因为已经编译为本地代码,避免了任何运行时编译
支持的场景 主要用于桌面应用、服务器应用(特别是使用 .NET Framework) 适用于 .NET Core 和 .NET 5+,优化跨平台应用的启动性能 适用于无 .NET 运行时的环境,如不希望在目标机器上安装 .NET 环境的情况 适用于不希望依赖 JIT 编译或需要最大化启动性能的场景
跨平台支持 主要用于 Windows,不能跨平台使用 跨平台支持(Windows, Linux, macOS) 跨平台支持(Windows, Linux, macOS) 跨平台支持(Windows, Linux, macOS)
部署复杂性 部署时需要生成并安装本地映像,可能增加复杂性 部署时需要将预编译的本地代码包含在应用程序中,较简单 部署时将应用程序和所有必要的依赖项打包到一个自包含的文件中,部署更简单 需要在编译阶段完成编译,部署时无需额外安装依赖,较简单
适用的框架版本 仅支持 .NET Framework 和 .NET Core (不支持 .NET 5+ 和以后版本) 仅适用于 .NET Core 和 .NET 5+ 适用于 .NET Core 和 .NET 5+,包括 .NET 6 和 .NET 7 适用于 .NET Core 和 .NET 5+,也适用于早期版本(如 .NET Framework)
是否支持动态代码生成 不支持动态代码生成(所有代码预先编译) 不支持动态代码生成(所有代码预先编译) 不影响代码生成,应用程序本身是自包含的 不支持动态代码生成,所有代码都在编译时生成
优点 提高启动性能,减少 JIT 编译负担,适合长期运行的应用程序 跨平台,减少 JIT 编译,适合现代的 .NET Core 和 .NET 5+ 应用 无需在目标机器上安装 .NET 运行时,简化部署 适用于对启动性能要求极高的场景,避免运行时编译
缺点 仅支持 Windows,且需要额外的磁盘空间来存储本地映像,更新复杂 需要在编译时生成本地代码,且对于较大的应用程序可能增加部署包体积 包体积较大,因为包含了应用程序和所有依赖项以及运行时 编译时间较长,生成的二进制文件较大,且对动态代码支持较差

总结

  • NGEN 是一个适用于 .NET Framework 和部分 .NET Core 的工具,通过提前编译代码来减少 JIT 编译负担,但它只支持 Windows 平台,并且需要安装在 GAC 中。
  • ReadyToRun (R2R) 是适用于 .NET Core 和 .NET 5+ 的技术,跨平台,提前编译并减少 JIT 编译,但需要将预编译的本地代码与程序集一起打包。
  • Self-contained Deployment 是一种完整的部署方式,将应用程序和所有的依赖项(包括运行时)一起打包,适用于无 .NET 环境的目标机器。
  • AOT (Ahead-of-Time) 编译是针对 .NET 5+ 引入的一种编译方式,它将整个应用程序及其依赖项编译为目标平台的本地代码,无需运行时的 JIT 编译,适用于需要高启动性能的场景。

每种技术在使用时需要根据项目需求、目标平台、应用场景等综合考虑。


 

posted @ 2024-12-20 21:13  suv789  阅读(9)  评论(0编辑  收藏  举报