2013-07-13 10 views
12

Napisałem małą symulację drapieżników-zdobycz w Javie. Nawet jeśli przepisy są dość skomplikowane i kończy się w układzie chaotycznym techniki wykorzystywane są proste:Java nie deterministyczny?

  • arytmetyki i decyzje dotyczące podstawowych typów danych
  • żadnych zewnętrznych bibliotek
  • żadne systemy zewnętrzne są ujęte
  • brak współbieżności występuje
  • bez użycia aktualnego czasu i daty

pomyślałem więc przy inicjalizacji systemu z identyczne parametry powinno dać identyczne wyniki, ale nie ma i zastanawiam się, dlaczego.

Kilka zdań na ten temat: Moja aplikacja używa Random s, ale dla tego testu inicjalizuję je wszystkie z podaną wartością, więc w moim rozumieniu powinny one dla każdego uruchomienia tworzyć te same wyjścia w tej samej kolejności.

Jestem Iterowanie poprzez Set s, i wiem, że kolejność iteracji Set nie jest zdefiniowany. Ale nie widzę żadnego powodu, dla którego Set, który jest wypełniony w tej samej kolejności tymi samymi wartościami, powinien zachowywać się inaczej w kilku seriach. Czy to?

Używam dużo float s. Typy danych, w których 1 + 1 = 1.9999999999725 są zawsze podejrzane, ale nawet jeśli ich zachowanie jest dla mnie dziwne, zawsze powinno być tak samo dziwne. Czyż nie?

Zbiór śmieci nie jest deterministyczny, ale dopóki nie polegam na destruktorach, powinienem być bezpieczny.

Jak wspomniano powyżej, nie ma współbieżności i nie ma typów danych w zależności od rzeczywistego czasu użytkowania.

Nie mogę odtworzyć tego zachowania w prostym przykładzie. Przechodząc przez mój kod, nie widzę niczego, co mogłoby być nieprzewidywalne. Czy którekolwiek z powyższych założeń jest błędne? Jakieś pomysły, których mógłbym przegapić?

Oto test, aby zweryfikować swoje założenia:

public static void main(String[] args) { 
    Random r = new Random(1); 
    Set<Float> s = new HashSet<Float>(); 
    for (int i = 0; i < 1000000; i++) { 
     s.add(r.nextFloat()); 
    } 

    float ret = 1; 
    int cnt = 0; 
    for (Float f : s) { 
     float multiply = 0.3f; 
     if (cnt++ % 2 == 0) { 
      multiply = 0.7f; 
     } 
     float f2 = (f * multiply); 
     ret += f2; 
    } 

    System.out.println(ret); 
} 

Wynika to zawsze w 242455.25 dla mnie.

+1

Funkcja czasu? – Mysticial

+1

Kiedy mówisz, że zainicjujesz losowe dane, masz na myśli wykorzystanie nasion? – tjameson

+0

To brzmi interesująco. Czy możemy rzucić okiem na twój kod? – kol

Odpowiedz

20

Możesz napisać program deterministyczny w Javie. Trzeba tylko wyeliminować potencjalne źródła niedeterminizmu.

Trudno powiedzieć, co może powodować niedeterminizm, nie widząc swojego prawdziwego kodu i konkretnych dowodów tego determinizmu.

Istnieje wiele metod bibliotecznych, które potencjalnie mogą być źródłem niedeterministycznego zachowania ... w zależności od sposobu ich użycia.

Na przykład wartość zwrócona przez Object.hashcode() (przy pierwszym wywołaniu instancji) nie jest deterministyczna. I to przenika do jakiejkolwiek biblioteki, która używa mieszania. Może to zdecydowanie wpłynąć na kolejność, w której zwracane są elementy HashSet lub HashMap ... jeśli klasa elementów nie zastępuje hashcode().

Generatory liczb losowych mogą być lub nie być deterministyczne. Jeśli są one pseudolosowe i są inicjowane za pomocą ustalonych nasion, sekwencja liczb wytwarzanych przez każdą z nich będzie deterministyczna.

Arytmetyka zmiennoprzecinkowa powinna być określona jako. Dla dowolnego (stałego) zbioru danych wejściowych do wyrażenia arytmetycznego wynik powinien zawsze być taki sam. (Nie jestem pewien, że determinizm z arytmetyki zmiennoprzecinkowej jest gwarantowana przez JLS, ale byłoby dziwne, gdyby potężny stało się w praktyce. Jak w ... używasz na łamanego sprzętu.)


FOLLOWUP ... na strictfp i niedeterminizm.

Według JLS 15.4:

„w wyrażeniu, które nie jest FP-surowe, pewną swobodę udzielana jest na realizację celu wykorzystać rozszerzony zakres wykładnik do reprezentowania wyników pośrednich; efekt netto, z grubsza mówiąc, że obliczenia mogą dać "poprawną odpowiedź" w sytuacjach, w których wyłączne użycie ustawionej wartości zmiennoprzecinkowej lub podwójnej wartości może spowodować przepełnienie lub niedopełnienie. "

nie dokładnie powiedzieć ile „pole manewru” realizacja ma w nie-FP-ścisłych wyrażeń. Jednak myślałem, że ta swoboda nie rozciągnie się na dopuszczenie niedeterministycznego zachowania. Pomyślałbym, że kompilator JIT na konkretnej platformie zawsze generowałby równoważny kod natywny dla tego samego wyrażenia, a ten kod byłby deterministyczny. (Nie widzę żadnego powodu dla niedeterminizmu ... chyba że sam sprzęt ma niedeterministyczny zmienny punkt). Innym możliwym źródłem niedeterminizmu może być to, że zachowanie skompilowanego i interpretowanego kodu JIT może być inne. Szczerze mówiąc, myślę, że byłoby to "szaleństwem", aby do tego doszło ... i myślę, że o nim słyszeliśmy.

Tak więc, podczas gdy ocena ekspresji niezwiązana z FP może być teoretycznie niedeterministyczna, uważam, że powinniśmy to zdyskontować, chyba że istnieją wyraźne dowody na to, że dzieje się to w praktyce.

(Zauważ, że mówię o prawdziwym braku determinizmu, a nie różnic platformy.)

+1

Determinizm arytmetyki zmiennoprzecinkowej nie jest gwarantowany bez użycia 'strictfp' - ale powinno to dotyczyć tylko platform. (+1) –

+0

Bez 'strctfp', implementacja jest wolna, aby czasami używać wyższej precyzji, a czasami nie. –

+0

@PaulBellora - Tak. Zmienność na różnych platformach to nie to samo co niedeterminizm. Jeśli miałeś prawdziwy niedeterminizm, to twój sprzęt HP jest wadliwy ... jeśli nie złamany. –

10

mam iteracji throu setów, a wiem, że kolejność Zestaw to powtórzyć nie jest zdefiniowana. Ale nie widzę żadnego powodu, dla którego zestaw wypełniony w tej samej kolejności tymi samymi wartościami powinien zachowywać się różnie w kilku seriach. Czy to?

Może. Implementacja jest darmowa, na przykład, lokalizacja obiektu w pamięci jako klucz do ukrytej tabeli mieszania. Może się to różnić w zależności od tego, kiedy uruchamiane jest usuwanie śmieci.

+0

@MichaelBulla - Jakiego JVM używasz? – tjameson

+0

@tjameson: Nie używam konkretnej maszyny JVM. To pytanie dotyczy tego, co * może * się wydarzyć, a nie co się dzieje. –

+0

To pytanie wydaje się być "zdarzeniem" i miałem nadzieję, że jeśli OP pozwoli nam zorientować się, z czego korzysta JVM, możemy sprawdzić uwagi dotyczące implementacji i sprawdzić, czy jest to potencjalnie spowodowane przez implementację. – tjameson

Powiązane problemy