强制.NET JIT编译器在应用程序启动时生成最优化的代码

15
我正在使用C#编写一款DSP应用程序(基本上是多轨编辑器)。我已经在不同的计算机上对其进行了相当长时间的分析,发现了一些“奇怪”的问题。
在我的家用电脑上,播放循环的第一次运行大约占用可用时间的50%-60%(我认为这是JIT执行其任务的原因),然后对于随后的循环,它降至稳定的5%消耗。问题是,如果我在较慢的计算机上运行应用程序,则第一次运行将占用超过可用时间,导致播放被中断并弄乱输出音频,这是不可接受的。此后,它会下降到8%-10%的消耗率。
即使在第一次运行之后,应用程序仍会不时地调用一些耗时的例程(每2秒左右一次),这会导致稳定的5%消耗率经历非常短暂的20%-25%峰值。我注意到,如果让应用程序运行一段时间,这些峰值也会下降到7%-10%。(我不确定是由于JIT重新编译了这些代码部分)。
因此,我在JIT方面遇到了严重的问题。尽管该应用程序即使在非常慢的计算机上也会表现出色,但是这些“编译风暴”将成为一个大问题。我正在尝试弄清楚如何解决此问题,并想出了一个想法,即使用一个属性标记所有“敏感”例程,在启动时告诉应用程序在它们真正需要时“挤压”它们,以便它们被完全优化。但这只是一个想法(我也不太喜欢它),我想知道是否有更好的解决整个问题的方法。
我很想听听你们的想法。
(NGEN该应用程序不是一个选项,我喜欢并希望获得所有JIT优化。)
编辑:
内存消耗和垃圾收集不是问题,我正在使用对象池,播放期间的最大峰值内存为304 Kb。

2
如果您能说出为什么NGEN不是一个选项,那将会有所帮助;理解问题是工作的一半... - Marc Gravell
1
首先,您需要证明问题所在。持续的暂停与JIT无关。一旦代码被访问过至少一次,JIT就没有发挥任何作用。您需要进行剖析。 - Marc Gravell
1
@dthorpe:我不确定那是否正确。JIT可以做的一件事是用对底层字段的直接访问替换属性调用,但这只能在运行时完成,而不能在NGEN时间完成。 - Steven Sudit
1
@Steven:用直接引用替换间接引用不需要执行分析。我认为Trap所指的是Java Hotspot优化器,在.NET领域似乎还没有出现。 - dthorpe
3
@Steven:是的,你说得对:NGEN可能会在内联JIT不会的情况下为间接引用生成代码。请注意,.NET 2.0及以后版本中的NGEN消除了.NET 1.x中臭名昭著的许多间接引用。这仍然没有改变我的陈述:.NET JIT编译器不会根据执行分析重新编译已经编译好的代码。上面评论中Trap问的是.NET JIT是否可以在代码执行几次后重新优化/重新编译为本机代码。我不知道.NET中是否有类似的功能。 - dthorpe
显示剩余8条评论
4个回答

18
你可以使用 PrepareMethod 方法在应用程序初始化期间触发JIT编译器编译整个程序集集合(无需使用 NGen)。
这个解决方案在这里有更详细的描述:强制在运行时进行JIT编译

强制进行即时编译而不实际执行方法,真是太聪明了!谢谢! - Loudenvier

4

初速度似乎确实像是Fusion+JIT,这可以通过ILMerge(用于Fusion)和NGEN(用于JIT)来帮助;您可以在启动时播放静音曲目,以便这些工作不会影响用户的使用体验。

NGEN是一个很好的选择;难道你不能使用它吗?

您提到的问题在初始加载后听起来与JIT无关。可能与垃圾收集有关。

您尝试过进行分析吗?包括CPU和内存(收集)?


对我来说,不使用ngen的唯一原因是它在安装过程中需要管理员权限,因此不适用于企业网络上的每个用户安装。也许这也是OP的原因? - Dirk Vollmar
为什么不在安装时将NGEN作为一个选项呢?如果您拥有管理员权限,就执行它。否则警告用户并不要执行它。 - Nic Wise

3
正如Marc所提到的,持续的波峰不像是JIT问题。还有其他需要注意的事项:
  • 垃圾回收 - 在音频处理期间是否分配了内存?如果产生了很多垃圾,甚至是在第0代垃圾回收中幸存的对象,这可能会导致明显的波峰。听起来您正在进行某种预分配,但要小心库代码中的隐藏分配(即使foreach循环也会分配!)

  • Denormals问题。在处理非常小的浮点数时,某些类型的处理器存在问题,可能会导致CPU波峰。请参见http://www.musicdsp.org/files/denormal.pdf了解详细信息。

编辑:

即使您不想使用NGen,也请至少比较一个已使用NGen的版本,以便了解JIT编译的差异。


1
有趣。我从未想到非规格化数会成为如此巨大的速度障碍。 - Jeffrey Hantin

2

如果您认为JIT影响了您的应用程序,请使用NGEN预编译您的应用程序,并再次运行测试。已经通过NGEN编译的代码中没有JIT开销。如果您仍然在NGEN应用程序中看到峰值,则说明它们不是由JIT引起的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接