C#是'types检查结构奇怪的.NET 4.0 x86优化行为
更新:我已经提交了Microsoft Connect的错误报告 ,请投票!
更新2:微软已经将bug报告标记为已修复
由微软于18/08/2010在17:25发布
该错误将在未来版本的运行时中修复。 恐怕现在判断是否会在服务包或下一个主要版本中还为时过早。
由于升级到VS2010,我得到一些非常奇怪的行为与“is”关键字。
下面的程序(test.cs)在debugging模式(对于x86)编译时输出True,在(对于x86)优化编译时输出False。 编译x64或AnyCPU中的所有组合会给出预期结果,即True。
在.NET 3.5下编译的所有组合都会给出预期的结果,True。
我正在使用下面的batch file(runtest.bat)来编译和testing使用编译器.NET框架的各种组合的代码。
- 有没有人在.NET 4.0下看到过这种types的问题?
- 当运行runtests.bat时,其他人是否在计算机上看到和我一样的行为?
-
@ $ @#$?
- 有没有解决这个问题?
test.cs中
using System; public class Program { public static bool IsGuid(object item) { return item is Guid; } public static void Main() { Console.Write(IsGuid(Guid.NewGuid())); } }
runtest.bat
@echo off rem Usage: rem runtest -- runs with csc.exe x86 .NET 4.0 rem runtest 64 -- runs with csc.exe x64 .NET 4.0 rem runtest v3.5 -- runs with csc.exe x86 .NET 3.5 rem runtest v3.5 64 -- runs with csc.exe x64 .NET 3.5 set version=v4.0.30319 set platform=Framework for %%a in (%*) do ( if "%%a" == "64" (set platform=Framework64) if "%%a" == "v3.5" (set version=v3.5) ) echo Compiler: %platform%\%version%\csc.exe set csc="C:\Windows\Microsoft.NET\%platform%\%version%\csc.exe" set make=%csc% /nologo /nowarn:1607 test.cs rem CS1607: Referenced assembly targets a different processor rem This happens if you compile for x64 using csc32, or x86 using csc64 %make% /platform:x86 test.exe echo =^> x86 %make% /platform:x86 /optimize test.exe echo =^> x86 (Optimized) %make% /platform:x86 /debug test.exe echo =^> x86 (Debug) %make% /platform:x86 /debug /optimize test.exe echo =^> x86 (Debug + Optimized) %make% /platform:x64 test.exe echo =^> x64 %make% /platform:x64 /optimize test.exe echo =^> x64 (Optimized) %make% /platform:x64 /debug test.exe echo =^> x64 (Debug) %make% /platform:x64 /debug /optimize test.exe echo =^> x64 (Debug + Optimized) %make% /platform:AnyCPU test.exe echo =^> AnyCPU %make% /platform:AnyCPU /optimize test.exe echo =^> AnyCPU (Optimized) %make% /platform:AnyCPU /debug test.exe echo =^> AnyCPU (Debug) %make% /platform:AnyCPU /debug /optimize test.exe echo =^> AnyCPU (Debug + Optimized)
检测结果
运行runtest.bat时,我在Win7 x64安装中得到以下结果。
> 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;博士
我做了一个类似的例子,
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优化器错误。 不能完全放在手指上,它会严重地优化代码。 但是在我看来,当它优化拳击转换时,它会遇到麻烦。 非常严重的错误,坦率地说。
这个bug已经修复了,我再也不能再重复它了。 我当前版本的clrjit.dll是4.0.30319.237date为2011年5月17日。我无法确切地知道什么更新修复它。 2011年8月5日我收到了一个安全更新,更新clrjit.dll到版本235,date为4月12日,这将是最早的。
要回答最后一个问题,您可以将MethodImpl
属性的MethodImpl
选项添加到您的IsGuid
方法中,作为解决此问题的解决方法。
我只是做了一个简单的testing,在.NET 4.0上的x86debugging和发布configuration之间切换,这似乎解决了这个问题。 我还没有运行你的runtests.bat。
如果还没有提交,您还应该在Connect中提交问题,并将其与您的问题链接起来。
这不是很酷。 我认为你应该提交一个错误。 (connect.microsoft.com)
此外,这似乎工作(我只testing你的失败案例虽然):
public static bool IsGuid(object item) { return item.GetType() == typeof(Guid); }
除了几个小数点以外,reflection器指示的唯一区别在于IsGuid方法:
DEBUG:
.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 }
我不够stream利解释这些差异,但这是一个社区wiki的答案,所以也许别人可以启发我们?
改变方法,使其通用作品围绕(可能的错误?)
public static bool IsGuid<T>(T item) { return item is Guid; }
正如强制它成为一个本地variables(但它必须在方法中使用,以防止优化踢):
public static bool IsGuid(object item) { bool a = item is Guid; a.ToString(); return a; }
这是我在XP SP3(x86)上的结果:
>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)
关于这个结果有趣的一点是,.NET 4目标在AnyCPU上运行(作为x86运行),但在AnyCPU上运行(以x64运行)。
有趣的是,这个工作正确:
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