Jestem nowy w Javie i zeszłej nocy uruchomiłem jakiś kod, co mnie bardzo niepokoiło. Budowałem prosty program do wyświetlania każdego wyjścia X w pętli for i zauważyłem MASYWNY spadek wydajności, kiedy użyłem modułu jako variable % variable
vs variable % 5000
lub co tam. Czy ktoś może mi wyjaśnić, dlaczego to jest i co go powoduje? Więc mogę być lepszy ...
Oto „wydajny” kod (przepraszam, jeśli mam trochę niepoprawnej składni, nie jestem teraz na komputerze z kodem)
long startNum = 0;
long stopNum = 1000000000L;
for (long i = startNum; i <= stopNum; i++){
if (i % 50000 == 0) {
System.out.println(i);
}
}
Oto „nieefektywny kod”
long startNum = 0;
long stopNum = 1000000000L;
long progressCheck = 50000;
for (long i = startNum; i <= stopNum; i++){
if (i % progressCheck == 0) {
System.out.println(i);
}
}
Pamiętaj, że miałem zmienną daty do pomiaru różnic, a gdy stała się wystarczająco długa, pierwsza zajęła 50 ms, a druga 12 sekund lub coś w tym rodzaju. Być może będziesz musiał zwiększyć stopNum
lub zmniejszyć, progressCheck
jeśli Twój komputer jest bardziej wydajny niż mój, czy co.
Szukałem tego pytania w Internecie, ale nie mogę znaleźć odpowiedzi, może po prostu nie zadaję tego poprawnie.
EDYCJA: Nie spodziewałem się, że moje pytanie będzie tak popularne, doceniam wszystkie odpowiedzi. Przeprowadziłem test porównawczy w każdej połowie czasu, a nieefektywny kod trwał znacznie dłużej, 1/4 sekundy w porównaniu do 10 sekund. To prawda, że używają println, ale obaj robią to samo, więc nie wyobrażam sobie, że to by to wypaczyło, zwłaszcza że rozbieżność jest powtarzalna. Jeśli chodzi o odpowiedzi, ponieważ jestem nowy w Javie, pozwolę głosom zdecydować, która odpowiedź jest najlepsza. Spróbuję wybrać jeden do środy.
EDYCJA 2: Dziś wieczorem zrobię kolejny test, w którym zamiast modułu, po prostu inkrementuje zmienną, a kiedy osiągnie progressCheck, wykona jedną, a następnie zresetuje tę zmienną do 0. dla trzeciej opcji.
EDYCJA 3.5:
Użyłem tego kodu, a poniżej pokażę moje wyniki. Dziękujemy wszystkim za wspaniałą pomoc! Próbowałem także porównać krótką wartość długiego z 0, więc wszystkie moje nowe kontrole zdarzają się zawsze „65536” razy, co czyni je równymi w powtórzeniach.
public class Main {
public static void main(String[] args) {
long startNum = 0;
long stopNum = 1000000000L;
long progressCheck = 65536;
final long finalProgressCheck = 50000;
long date;
// using a fixed value
date = System.currentTimeMillis();
for (long i = startNum; i <= stopNum; i++) {
if (i % 65536 == 0) {
System.out.println(i);
}
}
long final1 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
//using a variable
for (long i = startNum; i <= stopNum; i++) {
if (i % progressCheck == 0) {
System.out.println(i);
}
}
long final2 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
// using a final declared variable
for (long i = startNum; i <= stopNum; i++) {
if (i % finalProgressCheck == 0) {
System.out.println(i);
}
}
long final3 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
// using increments to determine progressCheck
int increment = 0;
for (long i = startNum; i <= stopNum; i++) {
if (increment == 65536) {
System.out.println(i);
increment = 0;
}
increment++;
}
//using a short conversion
long final4 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
for (long i = startNum; i <= stopNum; i++) {
if ((short)i == 0) {
System.out.println(i);
}
}
long final5 = System.currentTimeMillis() - date;
System.out.println(
"\nfixed = " + final1 + " ms " + "\nvariable = " + final2 + " ms " + "\nfinal variable = " + final3 + " ms " + "\nincrement = " + final4 + " ms" + "\nShort Conversion = " + final5 + " ms");
}
}
Wyniki:
- naprawiono = 874 ms (zwykle około 1000 ms, ale szybciej, ponieważ jest to moc 2)
- zmienna = 8590 ms
- zmienna końcowa = 1944 ms (było ~ 1000 ms przy użyciu 50000)
- przyrost = 1904 ms
- Krótka konwersja = 679 ms
Nic dziwnego, ze względu na brak podziału krótka konwersja była o 23% szybsza niż „szybka”. Warto to zauważyć. Jeśli chcesz coś pokazywać lub porównywać co 256 razy (lub mniej więcej tam), możesz to zrobić i użyć
if ((byte)integer == 0) {'Perform progress check code here'}
JEDNA KOŃCOWA UWAGA: użycie modułu w „Ostatecznej deklarowanej zmiennej” z wartością 65536 (niezbyt ładną liczbą) było o połowę mniejsze (wolniejsze) niż ustalona wartość. Gdzie wcześniej było to porównywanie z tą samą prędkością.
final
przedprogressCheck
zmienną, oba biegną ponownie z tą samą prędkością. To prowadzi mnie do przekonania, że kompilatorowi lub JITowi udaje się zoptymalizować pętlę, gdy wie, żeprogressCheck
jest stała.