Can C#和#x27;是';在.NET4上,操作员在释放模式优化下受到影响?
下面是一个简单的测试夹具。它在调试版本中成功,但在发布版本中失败(VS2010、.NET4解决方案、x64): 代码优化似乎造成了一些破坏;如果我在发布版本中禁用它,它也可以工作。这让我感到相当困惑。下面,我使用ILDASM分解了构建的2个版本: 调试IL:Can C#和#x27;是';在.NET4上,操作员在释放模式优化下受到影响?,c#,optimization,compiler-construction,il,C#,Optimization,Compiler Construction,Il,下面是一个简单的测试夹具。它在调试版本中成功,但在发布版本中失败(VS2010、.NET4解决方案、x64): 代码优化似乎造成了一些破坏;如果我在发布版本中禁用它,它也可以工作。这让我感到相当困惑。下面,我使用ILDASM分解了构建的2个版本: 调试IL: .method public hidebysig instance bool IsDateTime(object o) cil managed { // Code size 15 (0xf) .maxstack 2
.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 framework解决了这个问题,但这可能只是一个侥幸。我发现这种行为有点令人不安,有人能解释为什么编译器会认为进行一个产生不同可观察行为的优化是安全的吗
提前感谢。它与C#编译器无关,IL是相同的。您在.NET 4.0抖动优化器中发现了一个错误。您可以在VisualStudio中重新编程。工具+选项、调试、常规,取消选中“在模块加载时抑制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日。就流量控制而言,存储和加载本质上是一个nop,但可能会以某种方式对一些CPU缓存进行处理。实际的流只是将参数加载到堆栈上,检查它是否是实例(返回null或实例),将null推送到堆栈上,并比较(大于),从而在堆栈上留下布尔值 现在抖动对它的作用完全是另一回事了(这取决于您使用的平台。抖动会以性能为名做各种疯狂的事情(我们的团队最近受到了打击,因为尾部调用优化改变为跨域优化,打破了GetCallingAssembly())。抖动可能是内联IsDateTime,注意到它不可能不是DateTime,而只是将true推到堆栈上 您的发布版本也可能针对一个稍有不同的框架,因此测试程序集中的DateTime不是测试程序集中的DateTime
我意识到这并不能回答为什么你的代码会被破坏。这个bug已经被Jacob Stanley发现了。Jacob已经发现了,Microsoft已经确认它确实是CLR JIT中的一个bug。Microsoft不得不说: 这个错误将在运行时的未来版本中修复。我恐怕现在就说它将出现在service pack还是下一个主要版本中还为时过早 再次感谢您报告此问题 通过将以下属性添加到
TestChecker()
,您应该能够解决该错误:
作为参考,我查了一下mono
- Mono JIT编译器版本2.6.7(Debian 2.6.7-3ubuntu1)
- Mono JIT编译器版本2.8.2(sehe/d1c74ad-Fri-Feb 18 21:46:52 CET 2011)
.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
没有优化也是一样的
以下是mono针对该IL的jitted代码的结果:
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
00000130:
130:55%推压ebp
131:8b ec mov%esp,%ebp
133:53%ebx
134:56%esi
135:83 ec 10子$0x10,%esp
138:e8 00呼叫13d
13d:50亿波普%ebx
13e:81 c3 03 00添加$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
153:8b 45 f4 mov-0xc(%ebp),%eax
156:80亿mov(%eax),%eax
158:80亿mov(%eax),%eax
15a:8b 40 08 mov 0x8(%eax),%eax
15d:8b 48 08 mov 0x8(%eax),%ecx
160:8b 93 100 mov 0x10(%ebx),%edx
166:33 c0异或%eax,%eax
168:3b ca cmp%edx,%ecx
16a:0f 45 f0 cmovne%eax%esi
16d:85 f6测试%esi,%esi
16f:0f 97 c0设定值%al
172:0f b6 c0 movzbl%al,%eax
175:8D65 f8 lea-0x8(%ebp),%esp
178:5e pop%esi
179:50亿波普%ebx
17a:c9离开
17b:c3 ret
17c:8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
IsValid方法是从哪里来的?方法没有在C代码中显示出来。对不起,在ILDASM转储完成后,我重新命名了该方法;命名现已修复。对我来说,闻起来像是bug…非常好的发现你也不需要NUnit,一个简单的
.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
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