我可以停止 .NET 4 执行尾调用消除吗?

问题描述 投票:0回答:3

我们正在将应用程序迁移到 .NET 4.0(从 3.5)。我们遇到的问题之一只有在非常特定的条件下才能重现:

  • 仅在发布版本中
  • 仅在启用优化和/或调试信息设置为仅 pdb 的情况下。

我的意思是,如果我禁用优化并且将调试信息设置为完整,问题就会消失。

相关代码在启用优化等的发布模式下在.NET 3.5 上运行良好,并且已经运行了很长时间。

我真的不想暗示 C# 编译器中存在错误,所以我的问题是是否有任何技术可以用来追踪我们可能做错了什么而导致不正确的优化?

我正在尝试将这个问题缩小到一个小测试用例,以便我可以在这里发布一些代码。

编辑:

我已将问题追踪到以下位置:

我们在 Form 的构造函数中有这段代码:

public ConnectionForm()
{
    LocalControlUtil.Configure("ConnectionForm", "Username", usernameLabel);
    LocalControlUtil.Configure("ConnectionForm", "Password", passwordLabel);
    LocalControlUtil.Configure("ConnectionForm", "Domain", domainLabel);
    LocalControlUtil.Configure("ConnectionForm", "Cancel", cancelButton);
    LocalControlUtil.Configure("ConnectionForm", "OK", okButton);
}

这些调用是针对一些自定义本地化代码的。该窗体的构造函数是从另一个程序集中调用的。

LocalControlUtil.Configure
方法调用
Assembly.GetCallingAssembly()
,它为上述所有调用返回正确的值,除了最后一个

我可以对上面的行重新排序,添加新行或删除当前行,但每次都是最后一行不起作用。

我假设这是 JIT 将最后一个方法调用内联到调用构造函数的位置(在另一个程序集中)。将

[MethodImpl(MethodImplOptions.NoInlining)]
添加到上面的构造函数可以解决问题。

有人知道为什么会发生这种情况吗?我觉得奇怪的是最后一行只能内联。这是 .NET 4.0 中的新行为吗?

编辑2:

我现在已经将范围缩小到尾部调用消除,我认为这是由 .NET 4 中的新尾部调用内容引起的

在上面的代码中,构造函数中对

LocalControlUtil.Configure
的最后一次调用被消除并放入调用方法中,该方法位于另一个程序集中。当该方法调用
Assembly.GetCallingAssembly
时,我们没有得到正确的程序集。

有什么方法可以阻止编译器(或 JIT 或执行此操作的任何东西)消除尾部调用?

c# .net optimization .net-4.0
3个回答
5
投票

我只是把它放在评论中,如果它不是太长的话,但是你尝试过吗:

public ConnectionForm()
{
  try
  {
    LocalControlUtil.Configure("ConnectionForm", "Username", usernameLabel);
    LocalControlUtil.Configure("ConnectionForm", "Password", passwordLabel);
    LocalControlUtil.Configure("ConnectionForm", "Domain", domainLabel);
    LocalControlUtil.Configure("ConnectionForm", "Cancel", cancelButton);
    LocalControlUtil.Configure("ConnectionForm", "OK", okButton);
  }
  catch
  {
    throw;
  }
}

抛出任何异常的事实使得这对于编写的代码来说几乎是一个空更改,但 try-catch 边界通常会阻止编译器和抖动方面的优化。


1
投票

我认为您已经走在寻找问题的正确轨道上......将其缩小到最小的测试用例几乎总是一个很好的第一步。

接下来将比较为每个版本生成的 IL,例如 Reflector。 阅读 IL 并非易事(除非您有丰富的经验),因此最大限度地减少您必须查看的代码量至关重要。

更新:我对此进行了更多思考,如果问题在 IL 级别无法辨别,则可能是在 JIT(即时)级别,这明显更难以调试。 我从来没有达到过那个水平,所以我对问题的那部分没有任何洞察力(如果涉及到的话)。


0
投票

不,你不能。

.NET 4.0 比 3.5 优化了更多的尾部调用,这是一件好事。我们的代码非常愚蠢。

© www.soinside.com 2019 - 2024. All rights reserved.