2015-06-30 22 views
9

Jestem nowicjuszem C# i napotkam problem z następującym kodem (mam docelową strukturę jako 4.5 i dodałem odniesienie do System.Numerics):Rozpoczęcie kompilacji C# 64-bitowej bez debugowania zachowuje się inaczej niż po uruchomieniu z debugowaniem (BigInteger)

using System; 
using System.Numerics; 

namespace Test 
{ 
    class Program 
    { 
     static BigInteger Gcd(BigInteger x, BigInteger y) 
     { 
      Console.WriteLine("GCD {0}, {1}", x, y); 
      if (x < y) return Gcd(y, x); 
      if (x % y == 0) return y; 
      return Gcd(y, x % y); 
     } 

     static void Main(string[] args) 
     { 
      BigInteger a = 13394673; 
      BigInteger b = 53578691; 
      Gcd(a, b); 
     } 
    } 
} 

Gdy build release rozpoczyna się debugowania (F5 w Visual Studio - a break-point na końcu programu, więc mogę zobaczyć wyjście), otrzymuję następujące wyjście:

GCD 13394673, 53578691 
GCD 53578691, 13394673 
GCD 13394673, 13394672 
GCD 13394672, 1 

Jednak, gdy kompilacja wydania zostanie uruchomiona bez debugowania Ging (Ctrl + F5), mam następujące:

GCD 13394673, 53578691 
GCD 53578691, 53578691 

dziwo jeśli dodać Console.ReadLine() na końcu programu to działa zgodnie z oczekiwaniami!

Jakieś pomysły, co to powoduje? Dzięki.

+3

Witamy w [Heisenbug] (https://en.wikipedia.org/wiki/Heisenbug) –

+0

Hmm ... Nie mogę tego odtworzyć w VS2015 - widzę pierwsze wyjście w obu przypadkach. –

+0

LINQPad zachowuje się w ten sam sposób - w zależności od tego, czy włączasz, czy wyłączasz optymalizację. Ciekawy. (Działa na Win 7, .NET 4.5.1) – germi

Odpowiedz

11

Jest to błąd optymalizatora jitter x64 w .NET 4.0 do 4.5.2. Charakteryzowanie go jest dość trudne, kodegen jest dość ciężki ze względu na użycie BigInteger. Jitter x64 ma historię błędów optymalizatora dla typów struct, takich jak BigInteger, więc jest to prawdopodobna przyczyna. Połączenie z możliwą optymalizacją ogona w tej metodzie jest najbardziej prawdopodobnym wyzwalaczem.

Zazwyczaj zalecam zgłaszanie takiego błędu, ale dni tego jittera są ponumerowane. Microsoft zdecydował się wycofać i przepisać go całkowicie. Dostępne w .NET 4.6 - VS2015, nazwa kodu projektu to RyuJIT. Nie ma tego błędu.

Kilka możliwych obejścia:

projektowe + właściwościom Budowanie kartę, Platforma docelowa = x86. Zmusza to program do działania w trybie 32-bitowym i wykorzystuje jitter x86, nie ma tego błędu.

lub wyłączyć optymalizację dla tej metody z atrybutem:

using System.Runtime.CompilerServices; 
    ... 
    [MethodImpl(MethodImplOptions.NoOptimization)] 
    static BigInteger Gcd(BigInteger x, BigInteger y) { 
     // etc... 
    } 

Który jest w porządku, podnoszenia ciężkich jest w klasie BigInteger więc wyłączenie optymalizacji nie będzie wpływać na czas wykonania.

+0

@HansPassant, ok. Ustawiłem cel na dowolny procesor i wstępnie 32-bitowy, więc nie mogłem odtworzyć błędu. zmienił cel i powtórzył błąd. Ale nadal był odtwarzany tylko za pomocą VS2013, a nie VS2012. –

+0

@HansPassant Jestem bardzo nowy dla C#, więc jest trochę rozczarowujące, że wpadłbym na taki błąd (przynajmniej nie był przerywany) - czy powiedziałbyś, że C# był "błędny" w porównaniu do C++? –

+1

Hehe, tak, masz pecha :) Programiści początkujący mają na to talent, nikt tak naprawdę nie pisze takiego kodu. Zauważ, że ten rodzaj kodu łatwo ulega awarii wraz z nazwą tej witryny, sprawiają, że liczby są wystarczająco duże, a spadnie na wyjątek StackOverflowException. Co nie jest błędem. Kompilatory C++ również mają wiele błędów, C# ogólnie trzyma się dobrze. Optymalizator jittera został napisany w języku C++ :) –