C#和#x27;是';对结构的类型检查-奇数.NET 4.0 x86优化行为
更新:我已经向Microsoft Connect提交了一份申请,请投赞成票 更新2:Microsoft已将错误报告标记为已修复 微软于2010年8月18日17:25发布 此错误将在一个小时内修复 运行时的未来版本。我是 恐怕现在说这个还为时过早 将在service pack或下一个service pack中 主要版本 自从升级到VS2010后,我在“is”关键字上遇到了一些非常奇怪的行为 下面的程序(test.cs)在调试模式下编译时输出True(对于x86),在使用优化进行编译时输出False(对于x86)。在x64或AnyCPU中编译所有组合将得到预期结果True 在.NET3.5下编译的所有组合都给出了预期的结果,True 我正在使用下面的批处理文件(runtest.bat)使用编译器.NET framework的各种组合编译和测试代码C#和#x27;是';对结构的类型检查-奇数.NET 4.0 x86优化行为,c#,optimization,compiler-construction,casting,struct,C#,Optimization,Compiler Construction,Casting,Struct,更新:我已经向Microsoft Connect提交了一份申请,请投赞成票 更新2:Microsoft已将错误报告标记为已修复 微软于2010年8月18日17:25发布 此错误将在一个小时内修复 运行时的未来版本。我是 恐怕现在说这个还为时过早 将在service pack或下一个service pack中 主要版本 自从升级到VS2010后,我在“is”关键字上遇到了一些非常奇怪的行为 下面的程序(test.cs)在调试模式下编译时输出True(对于x86),在使用优化进行编译时输出False
- 还有人在.NET4.0下见过这种问题吗
- 其他人在运行runtests.bat时是否在他们的计算机上看到与我相同的行为
- @$@#$??
- 有解决办法吗
> runtest 32 v4.0
Compiler: Framework\v4.0.30319\csc.exe
False => x86
False => x86 (Optimized)
True => x86 (Debug)
False => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)
> runtest 64 v4.0
Compiler: Framework64\v4.0.30319\csc.exe
False => x86
False => x86 (Optimized)
True => x86 (Debug)
False => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)
> runtest 32 v3.5
Compiler: Framework\v3.5\csc.exe
True => x86
True => x86 (Optimized)
True => x86 (Debug)
True => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)
> runtest 64 v3.5
Compiler: Framework64\v3.5\csc.exe
True => x86
True => x86 (Optimized)
True => x86 (Debug)
True => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)
tl;dr除了一些NOP,reflector指出唯一的区别在于IsGuid方法: 调试:
.method public hidebysig static bool IsGuid(object item) cil managed
{
.maxstack 2
.locals init (
[0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: isinst [mscorlib]System.Guid
L_0007: ldnull
L_0008: cgt.un
L_000a: stloc.0
L_000b: br.s L_000d
L_000d: ldloc.0
L_000e: ret
}
发布:
.method public hidebysig static bool IsGuid(object item) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: isinst [mscorlib]System.Guid
L_0006: ldnull
L_0007: cgt.un
L_0009: ret
}
我不能流利地解释这些差异,但这是一个社区维基答案,所以也许其他人可以启发我们
改变方法使其通用化可以解决这个问题(可能的bug?)
以下是我在XP SP3(x86)上的结果:
这个结果的有趣之处在于.NET 4目标在任何CPU(以x86运行)上失败,但在任何CPU(以x64运行)上工作。这并不酷。我认为你应该提交一个bug。(connect.microsoft.com) 此外,这似乎是可行的(不过我只测试了你的失败案例):
要回答上一个问题,您可以在
IsGuid
方法中添加MethodImpl
属性和选项methodimpoptions.noinline
,作为解决问题的解决方法
我刚刚做了一个简单的测试,在.NET4.0上为x86在调试和发布配置之间切换,这似乎解决了问题。不过我还没有运行你的runtests.bat
如果一个问题尚未提交,您还应在中提交一个问题,并将其与您的问题链接起来。我设计了一个类似的例子,但以同样的方式失败:
using System;
using System.Runtime.CompilerServices;
public class Program {
static void Main() {
Console.Write(Verify(Test.Create()));
Console.ReadLine();
}
//[MethodImpl(MethodImplOptions.NoInlining)]
static bool Verify(IDisposable item) {
return item is Test;
}
struct Test : IDisposable {
public void Dispose() { }
public static Test Create() { return new Test(); }
}
}
这是一个JIT优化器错误。不能完全把手指放在它上面,它极大地优化了代码。但在我看来,当它优化拳击转换时,它会遇到麻烦。坦白说,这是一个相当严重的错误
这个错误已经修复,我不能再重新编程了。我当前的clrjit.dll版本是4.0.30319.237,日期为2011年5月17日。我不知道到底是什么更新修复了它。我在2011年8月5日得到了一个安全更新,将clrjit.dll更新为235版,最早的日期是4月12日。有趣的是,这是正确的:
using System;
public class Program
{
public static bool IsGuid(object item)
{
return item is Guid;
}
public static void Main()
{
Guid s = Guid.NewGuid();
Console.Write(IsGuid(s));
}
}
以下是il的区别:
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 23 (0x17)
.maxstack 1
.locals init (valuetype [mscorlib]System.Guid V_0)
IL_0000: call valuetype [mscorlib]System.Guid [mscorlib]System.Guid::NewGuid()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: box [mscorlib]System.Guid
IL_000c: call bool Program::IsGuid(object)
IL_0011: call void [mscorlib]System.Console::Write(bool)
IL_0016: ret
} // end of method Program::Main
好奇。我看到了同样的行为,但目前无法解释。很有趣。这种行为并不局限于guid,是吗?这肯定是一个bug。提交一份bug报告(确保给他们这个页面的链接和描述),看起来是更新解决了这个问题;请参阅“问题2”。我想知道它是否与JIT编译器有关?我现在有更多的问题需要回答:)IL很好。您要比较的是jitted代码,因为它以不同的抖动生成不同的结果。这看起来像是一个jit错误。要查看jit代码,最好编写一个程序,在要检查的代码运行后暂停。运行它,然后在暂停时连接调试器。之所以会出现这种僵硬的情况,是因为抖动知道何时连接了调试器,如果有调试器,它可以生成更容易调试的代码。如果你想知道它在没有调试器的情况下做什么,那么你必须欺骗它。它也总是适用于我自己的空值类型。如果参数为“new Guid()”,则它也始终适用于“is Guid”。此时,我怀疑.NET 4.0 32位运行时的Guid.NewGuid()实现;Console.Write(IsGuid(a));似乎有效。+1有趣的是,NoInling解决了这个问题。对于我们来说,这并不是一个真正的选择,因为我们的代码库中可能有数百个这样的案例,跟踪它们将很困难。
MethodImpl
在我的案例中不起作用。我在做是在事件处理程序内部检查,我看到的不一致不是与结构不一致,而是与一个适当的类(跨越AppDomain边界)。@asbjornu,考虑到这些差异,你应该问一个新问题,包括所有细节。ão,是的,一旦我能够完全了解正在发生的事情,我会的。更多信息,请参见此:
public static bool IsGuid(object item)
{
bool a = item is Guid;
a.ToString();
return a;
}
>runtest
Compiler: Framework\v4.0.30319\csc.exe
False => x86
False => x86 (Optimized)
True => x86 (Debug)
False => x86 (Debug + Optimized)
False => AnyCPU
False => AnyCPU (Optimized)
True => AnyCPU (Debug)
False => AnyCPU (Debug + Optimized)
>runtest v3.5
Compiler: Framework\v3.5\csc.exe
True => x86
True => x86 (Optimized)
True => x86 (Debug)
True => x86 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)
public static bool IsGuid(object item)
{
return item.GetType() == typeof(Guid);
}
using System;
using System.Runtime.CompilerServices;
public class Program {
static void Main() {
Console.Write(Verify(Test.Create()));
Console.ReadLine();
}
//[MethodImpl(MethodImplOptions.NoInlining)]
static bool Verify(IDisposable item) {
return item is Test;
}
struct Test : IDisposable {
public void Dispose() { }
public static Test Create() { return new Test(); }
}
}
using System;
public class Program
{
public static bool IsGuid(object item)
{
return item is Guid;
}
public static void Main()
{
Guid s = Guid.NewGuid();
Console.Write(IsGuid(s));
}
}
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 23 (0x17)
.maxstack 1
.locals init (valuetype [mscorlib]System.Guid V_0)
IL_0000: call valuetype [mscorlib]System.Guid [mscorlib]System.Guid::NewGuid()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: box [mscorlib]System.Guid
IL_000c: call bool Program::IsGuid(object)
IL_0011: call void [mscorlib]System.Console::Write(bool)
IL_0016: ret
} // end of method Program::Main