【问题标题】:Forcing the .NET JIT compiler to generate the most optimized code during application start-up强制 .NET JIT 编译器在应用程序启动期间生成最优化的代码
【发布时间】:2010-10-19 13:37:36
【问题描述】:

我正在用 C# 编写一个 DSP 应用程序(基本上是一个多轨编辑器)。我已经在不同的机器上对它进行了很长一段时间的分析,我注意到了一些“好奇”的东西。

在我的家用机器上,播放循环的第一次运行占用了大约 50%-60% 的可用时间,(我认为这是由于 JIT 完成了它的工作),然后对于后续循环它会下降到5%的稳定消费。问题是,如果我在较慢的计算机上运行应用程序,第一次运行占用的时间超过可用时间,导致播放中断并弄乱输出音频,这是不可接受的。之后,它会下降到 8%-10% 的消耗。

即使在第一次运行之后,应用程序仍会不时调用一些耗时的例程(或多或少每 2 秒一次),这导致稳定的 5% 消耗经历了 20%-25% 的非常短的峰值。我注意到,如果我让应用程序运行一段时间,这些峰值也会下降到 7%-10%。 (我不确定是不是因为 JIT 重新编译了这些代码部分)。

所以,我对 JIT 有一个严重的问题。虽然应用程序即使在非常慢的机器上也能很好地运行,但这些“编译风暴”将是一个大问题。我试图弄清楚如何解决这个问题,我想出了一个想法,即用一个属性标记所有“合理”例程,该属性将告诉应用程序在启动期间预先“挤压”它们,因此在真正需要时会对其进行全面优化。但这只是一个想法(我也不太喜欢它),我想知道是否有更好的解决方案来解决整个问题。

我想听听你们的想法。

(NGEN 应用程序不是一个选项,我喜欢并想要我能得到的所有 JIT 优化。)

编辑:

内存消耗和垃圾回收不是问题,我使用的是对象池,播放期间的最大内存峰值为 304 Kb。

【问题讨论】:

  • 如果您说为什么 NGEN 不是一个选项会有所帮助;理解问题是成功的一半……
  • 那你首先要证明问题出在哪里。持续的暂停与 JIT 无关。一旦代码至少被访问过一次,JIT 就没有任何作用。你将不得不配置文件......
  • @Trap:不,.NET JITer 中没有运行时分析。
  • @dthorpe:我不确定这是否正确。 JIT 可以做的一件事是用直接访问底层字段来替换属性调用,但这只能在运行时完成,而不是在 NGEN 时间。
  • @Steven:是的,你是对的:NGEN 可以对内联 JIT 不会的间接引用进行代码生成。请注意,.NET 2.0 及更高版本中的 NGEN 消除了 .NET 1.x 中的 NGEN 臭名昭著的许多间接寻址。这仍然不会改变我的说法:.NET JIT 编译器不会基于执行分析执行已编译代码的重新编译。 Trap 在上面的评论中询问的是 .NET JIT 是否可以在代码执行几次后重新优化/重新编译为本机代码。我不知道 .NET 中有类似的东西。

标签: c# .net optimization compiler-construction jit


【解决方案1】:

初始速度确实听起来像Fusion+JIT,ILMerge(对于Fusion)和NGEN(对于JIT)会有所帮助;您总是可以在启动时通过系统播放无声曲目,这样就可以完成所有艰苦的工作,而用户不会注意到任何失真?

NGEN 是一个不错的选择;有没有原因你不能用?

您在初始加载之后提到的问题确实听起来与 JIT 相关。也许是垃圾收集。

您是否尝试过分析? CPU 和内存(集合)?

【讨论】:

  • 对我来说,不使用 ngen 的唯一原因是它在安装过程中需要管理权限,因此不适用于公司网络上的每个用户安装。也许这也是OP的原因?
  • 那么为什么不将 NGEN 作为安装选项呢?因此,如果您具有管理员权限,请执行此操作。否则警告用户不要这样做。
【解决方案2】:

正如 Marc 所说,持续的峰值听起来不像是 JIT 问题。其他需要注意的事项:

  • 垃圾收集 - 您是否在音频处理期间分配内存?如果您正在创建大量垃圾,甚至是在 Gen 0 集合中幸存的对象,这可能会导致明显的峰值。听起来您正在做某种预分配,但要注意库代码中的隐藏分配(即使是 foreach 循环也可以分配!)

  • 非规范化。在处理可能导致 CPU 峰值的非常小的浮点数时,某些类型的处理器存在问题。详情请见http://www.musicdsp.org/files/denormal.pdf

编辑:

即使您不想使用 NGen,至少也可以比较 NGen 的版本,这样您就可以了解 JITing 的不同之处

【讨论】:

  • 有趣。我不知道反常态是一个如此可怕的减速带。
【解决方案3】:

您可以在应用程序的初始化例程期间使用PrepareMethod ...方法触发 JIT 编译器来编译您的整个程序集(无需使用NGen)。

这里更详细地描述了这个解决方案:Forcing JIT Compilation During Runtime

【讨论】:

    【解决方案4】:

    如果您认为自己受到 JIT 的影响,请使用 NGEN 预编译您的应用并再次运行测试。 NGEN 编译的代码中没有 JIT 开销。如果您仍然在 NGEN 的应用中看到峰值,那么您知道它们不是由 JIT 引起的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-21
      • 2016-10-27
      • 1970-01-01
      • 2019-03-06
      • 2017-06-09
      • 1970-01-01
      • 2015-09-03
      相关资源
      最近更新 更多