我能阻止.net 4执行尾部调用消除吗?
本文关键字:调用 尾部 执行 net | 更新日期: 2023-09-27 17:50:24
我们正在将一个应用程序迁移到。net 4.0(从3.5)。我们遇到的一个问题只有在非常特殊的条件下才能重现:
- 仅在发布版本
- 仅启用优化和/或调试信息设置为pdb-only
我的意思是,如果我禁用优化和设置调试信息为完整,问题就会消失。
有问题的代码在。net 3.5上运行良好,在发布模式下启用了优化等功能,并且已经运行了很长时间。
我真的不想暗示c#编译器中有一个bug,所以我的问题是是否有任何技术我可以用来追踪我们可能做错了什么导致不正确的优化?
我正在尝试将这个问题缩小到一个小的测试用例,所以我可以在这里发布一些代码。
编辑:我已经找到了以下问题:
在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或任何这样做的)消除尾部调用?
我只是把它放在评论中,如果它不是太长,但是你试过吗:
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边界通常会阻止编译器和抖动的优化。
我认为你已经在正确的轨道上找到了问题…将测试范围缩小到最小的测试用例几乎总是很好的第一步。
接下来将比较在Reflector之类的东西中为每个版本生成的IL。阅读IL是非常重要的(除非你有丰富的经验),所以尽量减少你必须浏览的代码量是至关重要的。
UPDATE:我对此进行了更多的思考,如果问题在IL级别上无法识别,则可能是在JIT (Just In Time)级别,这明显更难以调试。我从来没有接触过这个层面,所以我对这部分问题没有任何见解(如果涉及到这一点)。
不行。
。NET 4.0比3.5优化了更多的尾部调用,这是一件好事。我们的代码太蠢了