C# 的“is”运算符会在 .NET 4 上的发布模式优化下受到影响吗?

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

下面是一个简单的测试夹具。它在调试版本中成功,在发布版本中失败(VS2010、.NET4 解决方案、x64):

[TestFixture]
public sealed class Test
{
    [Test]
    public void TestChecker()
    {
        var checker = new Checker();
        Assert.That(checker.IsDateTime(DateTime.Now), Is.True);
    }
}

public class Checker
{
    public bool IsDateTime(object o)
    {
        return o is DateTime;
    }
}

代码优化似乎造成了一些破坏;如果我在发布版本上禁用它,它也可以工作。这让我很困惑。下面,我使用 ILDASM 反汇编了构建的 2 个版本:

调试IL:

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       15 (0xf)
  .maxstack  2
  .locals init (bool V_0)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  isinst     [mscorlib]System.DateTime
  IL_0007:  ldnull
  IL_0008:  cgt.un
  IL_000a:  stloc.0
  IL_000b:  br.s       IL_000d
  IL_000d:  ldloc.0
  IL_000e:  ret
} // end of method Validator::IsValid

释放IL:

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       10 (0xa)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     [mscorlib]System.DateTime
  IL_0006:  ldnull
  IL_0007:  cgt.un
  IL_0009:  ret
} // end of method Validator::IsValid

似乎商店和负载都被优化掉了。针对早期版本的 .NET 框架使问题消失,但这可能只是侥幸。我发现这种行为有点令人不安,任何人都可以解释为什么编译器会认为进行产生不同可观察行为的优化是安全的吗?

c# optimization cil
4个回答
20
投票

这个错误已经在 Jacob Stanley 的这个 SO 问题中出现过。 Jacob 已经报告了该错误,并且 Microsoft 已确认这确实是 CLR JIT 中的错误。微软是这样说的:

此错误将在运行时的未来版本中修复。恐怕现在判断这是否会出现在服务包或下一个主要版本中还为时过早。

再次感谢您报告该问题。

您应该能够通过向

TestChecker()
添加以下属性来解决该错误:

[MethodImpl(MethodImplOptions.NoInlining)]

15
投票

与C#编译器无关,IL是相同的。 您在 .NET 4.0 抖动优化器中发现了一个错误。 您可以在 Visual Studio 中重现它。 工具 + 选项、调试、常规,取消选中“抑制模块加载时的 JIT 优化”选项并运行发布版本以重现故障。

我还没有足够仔细地观察它来识别错误。 它看起来很奇怪,它内联了方法并完全省略了装箱转换的代码。 机器代码与版本 2 抖动生成的代码有很大不同。

干净的解决方法并不那么容易,您可以通过抑制内联来实现。 像这样:

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
    public bool IsDateTime(object o) {
        return o is DateTime;
    }

您可以在 connect.microsoft.com 上报告该错误。 如果您不愿意,请告诉我,我会处理的。


没关系,那已经是已经完成了。 VS2010 SP1 附带的维护版本中未修复此问题。


此错误已修复,我无法再重现它。 我当前的 clrjit.dll 版本是 4.0.30319.237,日期为 2011 年 5 月 17 日。我无法确切地说出哪个更新修复了它。 我在 2011 年 8 月 5 日收到了安全更新,将 clrjit.dll 更新为修订版 235,日期为 4 月 12 日,这将是最早的日期。


4
投票

就流量控制而言,存储和加载本质上是一个 nop,但可能会以某种方式调整一些 CPU 缓存。 实际流程只是将参数加载到堆栈上,检查它是否是一个实例(返回 null 或实例),将 null 压入堆栈,然后比较(大于),这会导致布尔值留在堆栈上。

现在 JITter 用它做什么完全是另一个故事(并且取决于您使用的平台。JITter 会以性能的名义做各种疯狂的事情(我们的团队最近受到了打击,因为尾部调用优化更改为跨域边界进行优化,这会破坏 GetCallingAssembly())。 JITter 可能会内联 IsDateTime,注意到它不可能不是 DateTime,只是将 true 推入堆栈。

您的发布版本也可能针对稍微不同的框架,因此测试程序集中的日期时间不是测试程序集中的日期时间。

我意识到这并不能回答你的代码被破坏的原因。


2
投票
作为参考,我检查了单声道

    Mono JIT 编译器版本 2.6.7 (Debian 2.6.7-3ubuntu1)
  • Mono JIT 编译器版本 2.8.2(sehe/d1c74ad,欧洲中部时间 2011 年 2 月 18 日星期五 21:46:52)
两者都没有出现任何问题。这是2.8.2中优化后的IL

.method public hidebysig instance default bool IsDateTime (object o) cil managed { // Method begins at RVA 0x2130 // Code size 10 (0xa) .maxstack 8 IL_0000: ldarg.1 IL_0001: isinst [mscorlib]System.DateTime IL_0006: ldnull IL_0007: cgt.un IL_0009: ret } // end of method Checker::IsDateTime

没有优化是

完全一样

这是该 IL 的 mono 即时代码的结果:

00000130 <TestData_Checker_IsDateTime_object>: 130: 55 push %ebp 131: 8b ec mov %esp,%ebp 133: 53 push %ebx 134: 56 push %esi 135: 83 ec 10 sub $0x10,%esp 138: e8 00 00 00 00 call 13d <TestData_Checker_IsDateTime_object+0xd> 13d: 5b pop %ebx 13e: 81 c3 03 00 00 00 add $0x3,%ebx 144: 8b 45 0c mov 0xc(%ebp),%eax 147: 89 45 f4 mov %eax,-0xc(%ebp) 14a: 8b 75 0c mov 0xc(%ebp),%esi 14d: 83 7d 0c 00 cmpl $0x0,0xc(%ebp) 151: 74 1a je 16d <TestData_Checker_IsDateTime_object+0x3d> 153: 8b 45 f4 mov -0xc(%ebp),%eax 156: 8b 00 mov (%eax),%eax 158: 8b 00 mov (%eax),%eax 15a: 8b 40 08 mov 0x8(%eax),%eax 15d: 8b 48 08 mov 0x8(%eax),%ecx 160: 8b 93 10 00 00 00 mov 0x10(%ebx),%edx 166: 33 c0 xor %eax,%eax 168: 3b ca cmp %edx,%ecx 16a: 0f 45 f0 cmovne %eax,%esi 16d: 85 f6 test %esi,%esi 16f: 0f 97 c0 seta %al 172: 0f b6 c0 movzbl %al,%eax 175: 8d 65 f8 lea -0x8(%ebp),%esp 178: 5e pop %esi 179: 5b pop %ebx 17a: c9 leave 17b: c3 ret 17c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
    
© www.soinside.com 2019 - 2024. All rights reserved.