【发布时间】:2012-07-14 04:37:54
【问题描述】:
我们正在将应用迁移到 .NET 4.0(从 3.5)。我们遇到的问题之一只能在非常特定的条件下重现:
- 仅在发布版本中
- 仅在启用优化和/或调试信息设置为 pdb-only 的情况下。
我的意思是,如果我禁用优化并将调试信息设置为完整,问题就会消失。
有问题的代码在 .NET 3.5 上运行良好,在发布模式下启用了优化等,并且已经运行了很长时间。
我真的不想暗示 C# 编译器中存在错误,所以我的问题是我是否可以使用任何技术来追踪我们可能做错了什么导致不正确的优化?
我正在尝试将这个问题缩小到一个小测试用例,以便我可以在这里发布一些代码。
编辑:
我已将问题归结为以下几点:
我们在表单的构造函数中有这段代码:
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:
我现在已将其范围缩小到消除尾调用,我认为是由 new tail-call stuff in .NET 4 引起的。
在上面的代码中,构造函数中对LocalControlUtil.Configure的最后一次调用被消除并放入调用方法中,该方法在另一个程序集中。当方法调用Assembly.GetCallingAssembly 时,我们没有得到正确的程序集。
有什么方法可以阻止编译器(或 JIT 或其他任何行为)消除尾调用?
【问题讨论】:
-
前几天我确实在 SO 上看到了一个类似的问题,它肯定看起来编译器中存在错误,因为它优化不正确。所以,这是可能的,但据我了解,这种情况相当罕见。
-
我记得在博客中读到过这个。不知何故,编译器导致某些对象被过早地处理掉。该示例是一个秒表方法,它在失败之前只运行了 1 个循环。很遗憾,我没有帖子的链接。
-
你能告诉我们更多关于这个问题的细节吗?例如,当您更改优化设置时,浮点计算有多种不同的方式。线程计时、垃圾收集计时等可以通过多种方式进行更改。问题的本质是什么?
标签: c# .net optimization compiler-construction .net-4.0