Ostatnio zauważyłem, że deklarowanie tablicy zawierającej 64 elementy jest dużo szybsze (> 1000 razy) niż deklarowanie tego samego typu tablicy z 65 elementami.
Oto kod, którego użyłem do przetestowania tego:
public class Tests{
public static void main(String args[]){
double start = System.nanoTime();
int job = 100000000;//100 million
for(int i = 0; i < job; i++){
double[] test = new double[64];
}
double end = System.nanoTime();
System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
}
}
Ta przebiega w około 6 ms, jeśli zastąpi new double[64]
się new double[65]
trwa około 7 sekund. Ten problem staje się wykładniczo poważniejszy, jeśli praca jest rozłożona na coraz więcej wątków, stąd mój problem.
Ten problem występuje również w przypadku różnych typów tablic, takich jak int[65]
lub String[65]
. Ten problem nie występuje w przypadku dużych ciągów:, String test = "many characters";
ale zaczyna się pojawiać po zmianie naString test = i + "";
Zastanawiałem się, dlaczego tak jest i czy da się obejść ten problem.
System.nanoTime()
powinno być preferowane wSystem.currentTimeMillis()
przypadku testów porównawczych.byte
zamiastdouble
.Odpowiedzi:
Obserwujesz zachowanie spowodowane optymalizacjami wykonanymi przez kompilator JIT maszyny wirtualnej Java. To zachowanie jest powtarzalne wyzwalane przez tablice skalarne do 64 elementów i nie jest wyzwalane w przypadku tablic większych niż 64.
Zanim przejdziemy do szczegółów, przyjrzyjmy się bliżej głównej części pętli:
double[] test = new double[64];
Ciało nie ma żadnego wpływu (obserwowalne zachowanie) . Oznacza to, że poza wykonaniem programu nie ma znaczenia, czy ta instrukcja jest wykonywana, czy nie. To samo dotyczy całej pętli. Może się więc zdarzyć, że optymalizator kodu tłumaczy pętlę na coś (lub nic) z tym samym funkcjonalnym i różnym zachowaniem czasowym.
W przypadku testów porównawczych należy przynajmniej przestrzegać następujących dwóch wskazówek. Gdybyś to zrobił, różnica byłaby znacznie mniejsza.
Przejdźmy teraz do szczegółów. Nic dziwnego, że istnieje optymalizacja, która jest uruchamiana dla tablic skalarnych nie większych niż 64 elementy. Optymalizacja jest częścią analizy Escape . Umieszcza małe obiekty i małe tablice na stosie zamiast alokować je na stercie - lub nawet lepiej je całkowicie zoptymalizować. Więcej informacji na ten temat można znaleźć w następującym artykule Briana Goetza napisanym w 2005 roku:
Optymalizację można wyłączyć za pomocą opcji wiersza poleceń
-XX:-DoEscapeAnalysis
. Magiczną wartość 64 dla tablic skalarnych można również zmienić w wierszu poleceń. Jeśli wykonasz swój program w następujący sposób, nie będzie różnicy między tablicami z 64 i 65 elementami:java -XX:EliminateAllocationArraySizeLimit=65 Tests
Powiedziawszy to, zdecydowanie odradzam używanie takich opcji wiersza poleceń. Wątpię, czy robi to dużą różnicę w realistycznej aplikacji. Wykorzystałbym go tylko wtedy, gdybym był absolutnie przekonany o konieczności - a nie w oparciu o wyniki niektórych pseudo-benchmarków.
źródło
Różnice mogą wystąpić na wiele sposobów, w zależności od rozmiaru obiektu.
Jak stwierdził nosid, JITC może (najprawdopodobniej będzie) przydzielać małe „lokalne” obiekty na stosie, a wielkość odcięcia dla „małych” tablic może wynosić 64 elementy.
Alokowanie na stosie jest znacznie szybsze niż alokowanie w stercie, a co ważniejsze, stos nie musi być zbierany jako śmieci, więc obciążenie GC jest znacznie zmniejszone. (A dla tego przypadku testowego narzut GC wynosi prawdopodobnie 80-90% całkowitego czasu wykonania).
Co więcej, po przydzieleniu wartości na stosie JITC może przeprowadzić „eliminację martwego kodu”, ustalić, że wynik operacji
new
nigdy nie jest nigdzie używany, a po upewnieniu się, że nie ma żadnych skutków ubocznych, które zostałyby utracone, wyeliminować całynew
operację, a następnie (teraz pusta) sama pętla.Nawet jeśli JITC nie dokonuje alokacji stosu, jest całkowicie możliwe, że obiekty mniejsze niż pewien rozmiar mogą być alokowane na stercie inaczej (np. Z innej „przestrzeni”) niż większe obiekty. (Zwykle nie spowodowałoby to jednak tak dramatycznych różnic w czasie).
źródło