W tym kodzie, kiedy tworzę obiekt w main
metodzie, a następnie wywołuję metodę obiektów: ff.twentyDivCount(i)
(działa w 16010 ms), działa znacznie szybciej niż wywołanie go za pomocą tej adnotacji: twentyDivCount(i)
(działa w 59516 ms). Oczywiście, gdy uruchamiam go bez tworzenia obiektu, ustawiam metodę jako statyczną, aby można ją było wywołać w pliku main.
public class ProblemFive {
// Counts the number of numbers that the entry is evenly divisible by, as max is 20
int twentyDivCount(int a) { // Change to static int.... when using it directly
int count = 0;
for (int i = 1; i<21; i++) {
if (a % i == 0) {
count++;
}
}
return count;
}
public static void main(String[] args) {
long startT = System.currentTimeMillis();;
int start = 500000000;
int result = start;
ProblemFive ff = new ProblemFive();
for (int i = start; i > 0; i--) {
int temp = ff.twentyDivCount(i); // Faster way
// twentyDivCount(i) - slower
if (temp == 20) {
result = i;
System.out.println(result);
}
}
System.out.println(result);
long end = System.currentTimeMillis();;
System.out.println((end - startT) + " ms");
}
}
EDYCJA: Jak dotąd wydaje się, że różne maszyny dają różne wyniki, ale użycie JRE 1.8. * Jest tam, gdzie oryginalny wynik wydaje się być konsekwentnie odtwarzany.
+PrintCompilation +PrintInlining
pokazanoOdpowiedzi:
Używając JRE 1.8.0_45 uzyskuję podobne wyniki.
Dochodzenie:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
opcjami VM pokazuje, że obie metody są kompilowane i wbudowywanemain
jest bardzo różny, a metoda instancji jest bardziej agresywnie zoptymalizowana, szczególnie pod względem rozwijania pętliNastępnie ponownie przeprowadziłem test, ale z innymi ustawieniami rozwijania pętli, aby potwierdzić powyższe podejrzenie. Uruchomiłem twój kod z:
-XX:LoopUnrollLimit=0
a obie metody działają wolno (podobnie do metody statycznej z domyślnymi opcjami).-XX:LoopUnrollLimit=100
a obie metody działają szybko (podobnie do metody instancji z domyślnymi opcjami).Podsumowując, wydaje się, że przy domyślnych ustawieniach JIT hotspotu 1.8.0_45 nie jest w stanie rozwinąć pętli, gdy metoda jest statyczna (chociaż nie jestem pewien, dlaczego zachowuje się w ten sposób). Inne maszyny JVM mogą dawać inne wyniki.
źródło
Tylko nieudowodnione przypuszczenie oparte na odpowiedzi asyliów.
JVM używa progu do rozwijania pętli, który wynosi około 70. Z jakiegoś powodu statyczne wywołanie jest nieco większe i nie jest rozwijane.
Zaktualizuj wyniki
LoopUnrollLimit
poniżej 52, obie wersje są wolne.Jest to dziwne, ponieważ przypuszczałem, że statyczne wywołanie jest tylko nieco większe w wewnętrznej reprezentacji, a OP trafił w dziwny przypadek. Ale różnica wydaje się wynosić około 20, co nie ma sensu.
-XX:LoopUnrollLimit=51 5400 ms NON_STATIC 5310 ms STATIC -XX:LoopUnrollLimit=52 1456 ms NON_STATIC 5305 ms STATIC -XX:LoopUnrollLimit=71 1459 ms NON_STATIC 5309 ms STATIC -XX:LoopUnrollLimit=72 1457 ms NON_STATIC 1488 ms STATIC
Dla chętnych do eksperymentowania moja wersja może się przydać.
źródło
NON_STATIC
iSTATIC
, ale mój wniosek był słuszny. Naprawiono teraz, dziękuję.Gdy jest to wykonywane w trybie debugowania, liczby są takie same dla instancji i przypadków statycznych. Oznacza to ponadto, że JIT waha się przed kompilacją kodu do kodu natywnego w przypadku statycznym w taki sam sposób, jak w przypadku metody instancji.
Dlaczego to robi? Trudno powiedzieć; prawdopodobnie zrobiłoby to dobrze, gdyby była to większa aplikacja ...
źródło
Po prostu poprawiłem test i otrzymałem następujące wyniki:
Wynik:
Dynamic Test: 465585120 232792560 232792560 51350 ms Static Test: 465585120 232792560 232792560 52062 ms
UWAGA
Gdy testowałem je oddzielnie, uzyskałem ~ 52 sekundy na dynamiczne i ~ 200 sekund na statyczne.
Oto program:
public class ProblemFive { // Counts the number of numbers that the entry is evenly divisible by, as max is 20 int twentyDivCount(int a) { // Change to static int.... when using it directly int count = 0; for (int i = 1; i<21; i++) { if (a % i == 0) { count++; } } return count; } static int twentyDivCount2(int a) { int count = 0; for (int i = 1; i<21; i++) { if (a % i == 0) { count++; } } return count; } public static void main(String[] args) { System.out.println("Dynamic Test: " ); dynamicTest(); System.out.println("Static Test: " ); staticTest(); } private static void staticTest() { long startT = System.currentTimeMillis();; int start = 500000000; int result = start; for (int i = start; i > 0; i--) { int temp = twentyDivCount2(i); if (temp == 20) { result = i; System.out.println(result); } } System.out.println(result); long end = System.currentTimeMillis();; System.out.println((end - startT) + " ms"); } private static void dynamicTest() { long startT = System.currentTimeMillis();; int start = 500000000; int result = start; ProblemFive ff = new ProblemFive(); for (int i = start; i > 0; i--) { int temp = ff.twentyDivCount(i); // Faster way if (temp == 20) { result = i; System.out.println(result); } } System.out.println(result); long end = System.currentTimeMillis();; System.out.println((end - startT) + " ms"); } }
Zmieniłem też kolejność testu na:
public static void main(String[] args) { System.out.println("Static Test: " ); staticTest(); System.out.println("Dynamic Test: " ); dynamicTest(); }
I mam to:
Static Test: 465585120 232792560 232792560 188945 ms Dynamic Test: 465585120 232792560 232792560 50106 ms
Jak widać, jeśli dynamika jest wywoływana przed statyczną, prędkość statyczna drastycznie spada.
Na podstawie tego testu porównawczego:
PRAKTYCZNA ZASADA:
Java: kiedy używać metod statycznych
źródło
Proszę spróbować:
public class ProblemFive { public static ProblemFive PROBLEM_FIVE = new ProblemFive(); public static void main(String[] args) { long startT = System.currentTimeMillis(); int start = 500000000; int result = start; for (int i = start; i > 0; i--) { int temp = PROBLEM_FIVE.twentyDivCount(i); // faster way // twentyDivCount(i) - slower if (temp == 20) { result = i; System.out.println(result); System.out.println((System.currentTimeMillis() - startT) + " ms"); } } System.out.println(result); long end = System.currentTimeMillis(); System.out.println((end - startT) + " ms"); } int twentyDivCount(int a) { // change to static int.... when using it directly int count = 0; for (int i = 1; i < 21; i++) { if (a % i == 0) { count++; } } return count; } }
źródło