Windows 7 64位中的递归 [英] Recursion in Windows 7 64 bit

查看:95
本文介绍了Windows 7 64位中的递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个帮助器类

public static class DateTimeHelper
  {
    public static int GetMonthDiffrence(DateTime date1, DateTime date2)
    {
      if (date1 > date2)
      {
        return getmonthdiffrence(date2, date1);
      }
      else
      {
        return ((date2.year - date1.year) * 12) + (date2.month - date1.month);
      }      
    }
  }

该函数计算两个日期之间的两个月,它确实满足我的要求。
到目前为止,没有问题。

The function calculate the number of months between two dates, it do exactly what I want. So far there is no problem.

问题是,当我使用Windows 7 64位版本时,我总是得到相同的值 0

The problem is when I am on release and windows 7 64 bit I get always the same value "0"

当我深入研究问题时,我意识到在某个时候,由于递归调用,两个参数相等。

When I got deep down the problem, I got aware that at some point, and because of the recursive call the two parameters are equal.

仅当我午餐未构建到调试器上的发行版并在Windows 7 64位系统上吃午餐时,我才会重复此错误。

I repeat that I have this bug only if I lunch the release built un-attached to the debugger and on windows 7 64 bit.

任何人都可以有这个想法吗?如果需要,我需要一些链接以获取更多详细信息。

Can anyone have any idea of this comportment? And if so I need some links to get in more details.

这里是IL代码。 (我认为它可以帮助您了解更多信息)。

Here is the IL Code. (I think it can help to understand more)

.class public auto ansi abstract sealed beforefieldinit Helpers.DateTimeHelper
    extends [mscorlib]System.Object
{
    // Methods
    .method public hidebysig static 
        int32 GetMonthDiffrence (
            valuetype [mscorlib]System.DateTime date1,
            valuetype [mscorlib]System.DateTime date2
        ) cil managed 
    {
        // Method begins at RVA 0x6a658
        // Code size 52 (0x34)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldarg.1
        IL_0002: call bool [mscorlib]System.DateTime::op_GreaterThan(valuetype [mscorlib]System.DateTime, valuetype [mscorlib]System.DateTime)
        IL_0007: brfalse.s IL_0011

        IL_0009: ldarg.1
        IL_000a: ldarg.0
        IL_000b: call int32 Helpers.DateTimeHelper::GetMonthDiffrence(valuetype [mscorlib]System.DateTime, valuetype [mscorlib]System.DateTime)
        IL_0010: ret

        IL_0011: ldarga.s date2
        IL_0013: call instance int32 [mscorlib]System.DateTime::get_Year()
        IL_0018: ldarga.s date1
        IL_001a: call instance int32 [mscorlib]System.DateTime::get_Year()
        IL_001f: sub
        IL_0020: ldc.i4.s 12
        IL_0022: mul
        IL_0023: ldarga.s date2
        IL_0025: call instance int32 [mscorlib]System.DateTime::get_Month()
        IL_002a: ldarga.s date1
        IL_002c: call instance int32 [mscorlib]System.DateTime::get_Month()
        IL_0031: sub
        IL_0032: add
        IL_0033: ret
    } // end of method DateTimeHelper::GetMonthDiffrence
} 

编辑:

如果您想重现该问题,请使用以下测试程序:

Here is a test program if you wish to reproduce the issue:

class Program
  {
    static void Main(string[] args)
    {
      for (int i = 2000; i < 3000; i++)
      {
        var date1 = new DateTime(i, 1, 1);
        var date2 = new DateTime(i + 1, 1, 1);
        var monthdiff = DateTimeHelper.GetMonthDiffrence(date2, date1);
        if (monthdiff == 0)
          Console.WriteLine(string.Format("date1 => {0}, date2 => {1}, diff=> {2}", date2, date1, monthdiff.ToString()));
      }
      Console.WriteLine("done!");
      Console.ReadKey();
    }
  }

您必须在发布模式和64下构建progect位配置,然后转到生成结果的位置并执行程序。
事前感谢。
我最好的问候。

you have to build the progect on release mode and 64 bit configuration, and then go to the location of the built result and execute the program. Thanks before hand. My best regards.

推荐答案

我可以在Windows 7,.Net 4.5,Visual Studio 2012, x64目标,发布模式,已连接调试器,但禁用了抑制模块加载时的JIT优化。这似乎是尾部调用优化中的一个错误(这就是为什么只在x64上得到它的原因)。

I can replicate this behavior on Windows 7, .Net 4.5, Visual Studio 2012, x64 target, Release mode with debugger attached, but "Suppress JIT optimization on module load" disabled. This seems to be a bug in the tail call optimization (which is why you're getting it only on x64).

IL在这里并不重要,本机代码做。 GetMonthDiffrence()的相关代码部分是:

IL does not really matter here, the native code does. The relevant part of code for GetMonthDiffrence() is:

0000005e  cmp         rdx,rcx 
00000061  setg        al 
00000064  movzx       eax,al 
00000067  test        eax,eax 
00000069  je          0000000000000081 // else branch
0000006b  mov         rax,qword ptr [rsp+68h] 
00000070  mov         qword ptr [rsp+60h],rax 
00000075  mov         rax,qword ptr [rsp+60h] 
0000007a  mov         qword ptr [rsp+68h],rax 
0000007f  jmp         0000000000000012 // start of the method

重要的部分是4 mov 指令。他们尝试交换 [rsp + 68h] [rsp + 60h] (这是存储参数的位置) ,但是它们做错了,因此两者最终都具有相同的值。

The important part are the 4 mov instructions. They try to exchange [rsp+68h] and [rsp+60h] (which is where the parameters are stored), but they do it incorrectly, so both end up with the same value.

有趣的是,如果我删除对 Console.ReadKey()的调用, 从您的 Main()中,代码可以正常工作,因为调用 GetMonthDiffrence()是内联的,在这种情况下不会执行尾调用优化。

Interestingly, if I remove the call to Console.ReadKey() from your Main(), the code works fine, because the call to GetMonthDiffrence() is inlined and the tail call optimization is not performed in that case.

可能的解决方法是添加 [MethodImpl(MethodImplOptions.NoInlining) ] 到您的方法,这似乎禁用了尾部调用优化。

A possible workaround would be to add [MethodImpl(MethodImplOptions.NoInlining)] to your method, that seems to disable the tail call optimization.

我有在连接上提交了此错误

这篇关于Windows 7 64位中的递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆