.NET JIT潜在错误?
在Visual Studio中运行发行版并在Visual Studio外运行发行版时,以下代码提供了不同的输出。 我正在使用Visual Studio 2008并以.NET 3.5为目标。 我也尝试过.NET 3.5 SP1。
当在Visual Studio外部运行时,JIT应该启动。或者(a)C#中存在一些细微的缺失,或者(b)JIT实际上是错误的。 我怀疑JIT可能会出错,但我已经没有其他可能性了...
在Visual Studio中运行时输出:
0 0,
0 1,
1 0,
1 1,
在Visual Studio外部运行发行版时输出:
0 2,
0 2,
1 2,
1 2,
是什么原因?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Test
{
struct IntVec
{
public int x;
public int y;
}
interface IDoSomething
{
void Do(IntVec o);
}
class DoSomething : IDoSomething
{
public void Do(IntVec o)
{
Console.WriteLine(o.x.ToString() + " " + o.y.ToString()+",");
}
}
class Program
{
static void Test(IDoSomething oDoesSomething)
{
IntVec oVec = new IntVec();
for (oVec.x = 0; oVec.x < 2; oVec.x++)
{
for (oVec.y = 0; oVec.y < 2; oVec.y++)
{
oDoesSomething.Do(oVec);
}
}
}
static void Main(string[] args)
{
Test(new DoSomething());
Console.ReadLine();
}
}
}
这是一个JIT优化器错误。 它展开内部循环但不能正确更新oVec.y值:
for (oVec.x = 0; oVec.x < 2; oVec.x++) {
0000000a xor esi,esi ; oVec.x = 0
for (oVec.y = 0; oVec.y < 2; oVec.y++) {
0000000c mov edi,2 ; oVec.y = 2, WRONG!
oDoesSomething.Do(oVec);
00000011 push edi
00000012 push esi
00000013 mov ecx,ebx
00000015 call dword ptr ds:[00170210h] ; first unrolled call
0000001b push edi ; WRONG! does not increment oVec.y
0000001c push esi
0000001d mov ecx,ebx
0000001f call dword ptr ds:[00170210h] ; second unrolled call
for (oVec.x = 0; oVec.x < 2; oVec.x++) {
00000025 inc esi
00000026 cmp esi,2
00000029 jl 0000000C
当您让oVec.y增加到4时,该错误消失,这是太多的展开呼叫。
一种解决方法是这样的:
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
oDoesSomething.Do(new IntVec(x, y));
}
}
更新:2012年8月重新检查,此错误在版本4.0.30319抖动中得到修复。 但是在v2.0.50727抖动中仍然存在。 看起来他们不太可能在这么久之后在旧版本中修复这个问题。
我相信这是一个真正的JIT编译错误。 我会把它报告给微软,看看他们说了些什么。 有趣的是,我发现x64 JIT没有相同的问题。
这是我阅读的x86 JIT。
// save context
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
// put oDoesSomething pointer in ebx
00000006 mov ebx,ecx
// zero out edi, this will store oVec.y
00000008 xor edi,edi
// zero out esi, this will store oVec.x
0000000a xor esi,esi
// NOTE: the inner loop is unrolled here.
// set oVec.y to 2
0000000c mov edi,2
// call oDoesSomething.Do(oVec) -- y is always 2!?!
00000011 push edi
00000012 push esi
00000013 mov ecx,ebx
00000015 call dword ptr ds:[002F0010h]
// call oDoesSomething.Do(oVec) -- y is always 2?!?!
0000001b push edi
0000001c push esi
0000001d mov ecx,ebx
0000001f call dword ptr ds:[002F0010h]
// increment oVec.x
00000025 inc esi
// loop back to 0000000C if oVec.x < 2
00000026 cmp esi,2
00000029 jl 0000000C
// restore context and return
0000002b pop ebx
0000002c pop esi
0000002d pop edi
0000002e pop ebp
0000002f ret
这看起来像是一种优化对我不利...
我将您的代码复制到新的控制台应用程序中。
所以它是错误地生成代码的x86 JIT。 删除了我有关重新排序循环等的原始文本。这里的其他一些答案已经证实JIT在x86上正确地展开循环。
要解决这个问题,您可以将IntVec的声明更改为一个类,并且它适用于所有类型。
认为这需要继续MS Connect ....
-1给微软!
链接地址: http://www.djcxy.com/p/21063.html