JIT 优化和调试

如果尝试调试代码,则在该代码未优化时更易调试。 优化代码时,编译器和运行时会对发出的 CPU 代码进行更改,使其运行速度更快,但与原始源代码的映射不太直接。 如果映射不太直接,则调试器通常无法告诉你局部变量的值,代码步进和断点也可能无法按预期要求工作。

注意

有关 JIT(实时)调试的详细信息,请阅读此文档

.NET 中的优化如何工作

通常,“发布”生成配置会创建优化的代码,“调试”生成配置则不会。 Optimize MSBuild 属性控制是否指示编译器优化代码。

在 .NET 生态系统中,代码通过一个两步过程从源代码转换为 CPU 指令:首先,C# 编译器将键入的文本转换为称为 MSIL 的中间二进制格式,并将 MSIL 写入 .dll 文件。 随后,.NET 运行时将此 MSIL 转换为 CPU 指令。 这两个步骤都可以优化到一定程度,但 .NET 运行时执行的第二个步骤将执行更重要的优化。

“在模块加载时取消 JIT 优化(仅限托管)”选项

调试器提供了一个选项,该选项控制在启用优化的情况下编译的 DLL 加载到目标进程内部时所发生的情况。 如果未选中此选项(默认状态),则当 .NET 运行时将 MSIL 代码编译为 CPU 代码时,它会保留优化的启用状态。 如果选中该选项,则调试器将请求禁用优化。

若要找到“在模块加载时取消 JIT 优化(仅限托管)”选项,请选择“工具”>“选项”,然后选择“调试”节点下的“常规”页 。

取消 JIT 优化

何时应选中“取消 JIT 优化”选项?

从另一个源(例如 nuget 包)下载 DLL 并要​​调试此 DLL 中的代码时,请选中此选项。 为了使取消生效,还必须找到此 DLL 的符号 (.pdb) 文件。

如果只想调试本地生成的代码,最好将此选项保留为未选中状态,因为在某些情况下,启用此选项会大大减慢调试速度。 造成减速的原因有两个:

  • 经过优化的代码运行速度更快。 如果为大量代码关闭优化,则可能会增加性能影响。
  • 如果启用“仅我的代码”,则调试器甚至不会尝试为经过优化的 DLL 加载符号。 查找符号可能需要很长时间。

“取消 JIT 优化”选项的局限性

在两种情况下,启用此选项不起作用:

  1. 在将调试器附加到已在运行的进程的情况下,此选项对附加调试器时已加载的模块无效。

  2. 此选项对已预编译(或 ngen'ed)为本机代码的 DLL 无效。 但是,可以通过以下方式禁用预编译的代码:通过将环境变量“COMPlus_ReadyToRun”设置为“0”来启动进程。 这将指示 .NET Core 运行时禁用预编译的映像,从而迫使运行时实时编译框架代码。

    如果目标是 .NET Framework,则还需添加环境变量“COMPlus_ZapDisable”并将其设置为“1”。

通过将 "COMPlus_ReadyToRun": "0" 添加到 Properties\launchSettings.json 文件中的每个配置文件来对其进行设置:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:59694/",
      "sslPort": 44320
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "COMPlus_ReadyToRun": "0"
      }
    },
    "HttpLoggingSample": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "COMPlus_ReadyToRun": "0"
      },
      "applicationUrl": "https://localhost:5001;http://localhost:5000"
    }
  }
}