2013-07-05 4 views
2

У меня есть этот вспомогательный классРекурсия в 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); 
     }  
    } 
    } 

Функция расчета количества месяцев между двумя датами, он делает именно то, что я хочу. До сих пор нет никаких проблем.

Проблема заключается в том, когда я на выпуске и окна 7 64 бит я получаю всегда одинаковое значение «0»

Когда я глубоко проблемы, я в курсе, что в какой-то момент, и из-за рекурсивный вызов, оба параметра равны.

Повторяю, что у меня есть эта ошибка, только если я пообедаю, что релиз не был прикреплен к отладчику и к окнам 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 
} 

EDIT:

Вот тестовая программа, если вы хотите, чтобы воспроизвести проблему:

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-битную конфигурацию, а затем перейдите к местоположению построенного результата и выполните программу. Спасибо за руку. С наилучшими пожеланиями.

+0

Тест-программа отлично работает для меня (Windows 8, .Net 4.5, x64, выпуск сборка) –

+0

Зачем вам нужна рекурсия для этой задачи - это вопрос, который меня очень беспокоит. –

+0

Вы обедали программой из визуальной студии?, Если это так и для меня тоже. единственный раз, когда он не работает для меня, - это когда я обедаю exe непосредственно с диска. и это может быть проблемой в Windows 7. У меня нет окон 8 при моем просадке для тестирования. (.Net 4.0) – Swift

ответ

4

Я могу воспроизвести это поведение в Windows 7, .Net 4.5, Visual Studio 2012, x64 target, режиме деблокирования с прикрепленным отладчиком, но отключить оптимизацию JIT при загрузке модуля. Это, по-видимому, является ошибкой в ​​оптимизации хвостового вызова (именно поэтому вы получаете его только на x64).

ИЛ не имеет особого значения здесь, нативный код. Соответствующая часть кода для 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 

важной частью являются инструкции. Они пытаются обменять [rsp+68h] и [rsp+60h] (где хранятся параметры), но они делают это неправильно, поэтому оба имеют одинаковое значение.

Интересно, если я удалить вызов Console.ReadKey() из вашего Main(), код работает отлично, потому что вызов GetMonthDiffrence() является встраиваемой и оптимизация хвост вызов не выполняется в этом случае.

Возможным обходным путем было бы добавить [MethodImpl(MethodImplOptions.NoInlining)] к вашему методу, который, по-видимому, отключает оптимизацию хвостового вызова.

У меня есть submitted this bug on Connect.

+0

Они сказали, что не могут его воспроизвести. Я бы разместил его там, но, судя по всему, Microsoft Connect требует, чтобы я «входил», несмотря на то, что уже был подписан. Я мог только перепрограммировать его, когда «платформа цели» приложения была установлена ​​на x64. Не просто «предпочитают 32-битную», как я предполагал, они делают – Earlz

+1

Я только что воспроизвел эту проблему в Windows 8.1 Preview с .NET 4.5.1 :-(. К сожалению, слишком поздно, чтобы собрать исправление вместе для 4.5.1. Убедимся, что мы исправим его в следующем раунде. –

+0

Замечание: вы можете поместить [MethodImpl (MethodImplOptions.NoInlining)] перед методом GetMonthDifference, который предотвращает как inlining & tail-call (он находится в Системе. Пространство имен Runtime.CompilerServices) –

Смежные вопросы