我有这个助手类
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”
当我深入了解问题时,我意识到在某些时候,由于递归调用,两个参数是相等的。
我再说一遍,我有这个错误,只有当我在发布时没有附加到调试器并且在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 < 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,然后转到构建结果的位置并执行程序。 先谢谢你。 我最诚挚的问候。
答案 0 :(得分:4)
我可以在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]
(这是存储参数的位置),但他们做错了,所以两者都以相同的值结束。
有趣的是,如果我从您的Console.ReadKey()
中移除了对Main()
的调用,则代码可以正常工作,因为对GetMonthDiffrence()
的调用是内联的,并且未执行尾调用优化情况下。
一种可能的解决方法是将[MethodImpl(MethodImplOptions.NoInlining)]
添加到您的方法中,这似乎会禁用尾调用优化。