简体   繁体   English

Windows 7 64位中的递归

[英]Recursion in Windows 7 64 bit

I have this helper class 我有这个帮手课

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. 到目前为止,没有问题。

The problem is when I am on release and windows 7 64 bit I get always the same value "0" 问题是当我在发行版和Windows 7 64位上时,我总是得到相同的值“ 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. 当我深入研究问题时,我意识到在某个时候,由于递归调用,两个参数相等。

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. 我再说一遍,只有当我午餐该版本未附加到调试器上并且在Windows 7 64位上构建时,才会出现此错误。

Can anyone have any idea of this comportment? 谁能对这个举动有任何想法? And if so I need some links to get in more details. 如果是这样,我需要一些链接以获取更多详细信息。

Here is the IL Code. 这是IL代码。 (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
} 

EDIT: 编辑:

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();
    }
  }

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. 您必须在发布模式和64位配置上构建程序,然后转到构建结果的位置并执行程序。 Thanks before hand. 先谢谢你。 My best regards. 我最诚挚的问候。

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. 我可以在Windows 7,.Net 4.5,Visual Studio 2012,x64目标,带有调试器的发布模式下复制此行为,但是禁用了“在模块加载时抑制JIT优化”。 This seems to be a bug in the tail call optimization (which is why you're getting it only on x64). 这似乎是尾部调用优化中的错误(这就是为什么只在x64上得到它的原因)。

IL does not really matter here, the native code does. IL在这里并不重要,本机代码却无关紧要。 The relevant part of code for GetMonthDiffrence() is: GetMonthDiffrence()的代码的相关部分是:

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

The important part are the 4 mov instructions. 重要的部分是4个mov指令。 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. 他们尝试交换[rsp+68h][rsp+60h] (这是存储参数的位置),但是它们做错了,因此最终都具有相同的值。

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. 有趣的是,如果我从Main()删除对Console.ReadKey()的调用,则代码可以正常工作,因为对GetMonthDiffrence()的调用是内联的,并且在这种情况下不执行尾部调用优化。

A possible workaround would be to add [MethodImpl(MethodImplOptions.NoInlining)] to your method, that seems to disable the tail call optimization. 可能的解决方法是将[MethodImpl(MethodImplOptions.NoInlining)]添加到您的方法中,这似乎禁用了尾部调用优化。

I have submitted this bug on Connect . 我已在Connect上提交了此错误

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM