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

该函数计算两个日期之间的月数,它完全符合我的要求。 到目前为止没有问题。

问题是当我在发布时,Windows 7 64位我总是得到相同的值“0”

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

我再说一遍,我有这个bug,只有当我在发布时没有附加到调试器和Windows 7 64位上。

任何人都可以对这种竞争有任何想法吗? 如果是这样,我需要一些链接来获取更多细节。

这是IL代码。 (我认为它可以帮助理解更多)

 .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 } 

编辑:

如果您希望重现该问题,这是一个测试程序:

 class Program { static void Main(string[] args) { for (int i = 2000; i  {0}, date2 => {1}, diff=> {2}", date2, date1, monthdiff.ToString())); } Console.WriteLine("done!"); Console.ReadKey(); } } 

你必须在发布模式和64位配置上构建progect,然后转到构建结果的位置并执行程序。 先谢谢你。 我最诚挚的问候。

我可以在Windows 7,.Net 4.5,Visual Studio 2012,x64目标,附加调试器的发布模式下复制此行为,但禁用“禁止模块加载时JIT优化”。 这似乎是尾部调用优化中的一个错误(这就是为什么你只在x64上获得它)。

IL在这里并不重要,本机代码确实如此。 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 

重要的部分是4 mov指令。 他们试图交换[rsp+68h][rsp+60h] (这是存储参数的地方),但它们做错了,所以两者都以相同的值结束。

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

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

我在Connect上提交了这个错误 。