Dlaczego drukowanie „B” jest znacznie wolniejsze niż drukowanie „#”?


2746

Wygenerowałem dwie macierze 1000x 1000:

Pierwsza matryca: Oi #.
Druga matryca: Oi B.

Przy użyciu następującego kodu wykonanie pierwszej matrycy zajęło 8,52 sekundy:

Random r = new Random();
for (int i = 0; i < 1000; i++) {
    for (int j = 0; j < 1000; j++) {
        if(r.nextInt(4) == 0) {
            System.out.print("O");
        } else {
            System.out.print("#");
        }
    }

   System.out.println("");
 }

W przypadku tego kodu wykonanie drugiej macierzy zajęło 259,152 sekundy:

Random r = new Random();
for (int i = 0; i < 1000; i++) {
    for (int j = 0; j < 1000; j++) {
        if(r.nextInt(4) == 0) {
            System.out.print("O");
        } else {
            System.out.print("B"); //only line changed
        }
    }

    System.out.println("");
}

Jaki jest powód dramatycznie różnych czasów pracy?


Jak sugerowano w komentarzach, drukowanie System.out.print("#");zajmuje tylko 7.8871sekundy, a System.out.print("B");daje still printing....

Jak inni, którzy zauważyli, że działa dla nich normalnie, próbowałem na przykład Ideone.com i oba fragmenty kodu działają z tą samą prędkością.

Test kondycji:

  • Uruchomiłem ten test z Netbeans 7.2 z wyjściem na konsolę
  • Użyłem System.nanoTime()do pomiarów

62
Spróbuj zmienić rand.nextInt (4) == 0 na i <250, aby wyeliminować działanie generatora losowego. Być może zabraknie entropii, która spowalnia losowe generowanie
— fejese

3
Oba wydają się działać przez ten sam czas na moim komputerze, ~ 4 sekundy.
— Sotirios Delimanolis

155
jeśli sugerujesz, że drukowanie B zajmuje więcej czasu niż drukowanie # ... dlaczego nie próbujesz wydrukować całego B i wszystkich # zamiast polegać na losowej zmiennej r
— Kakarot

18
Na podstawie zaakceptowanej odpowiedzi najwyraźniej nie próbowałeś uruchomić jej z wyjściem przekierowanym do pliku lub / dev / null.
— Barmar

24
@fejese, Random () nie jest plikiem kryptograficznym, więc nie zużywa puli entropii.
— Podziel się

Odpowiedzi:


4070

Czysta spekulacja polega na tym, że używasz terminala, który próbuje zawijać słowa, a nie znaków, i traktuje je Bjako znak słowa, ale #jako znak inny niż słowo. Kiedy więc dotrze do końca linii i szuka miejsca, w którym można ją przełamać, widzi #niemal natychmiastowe i szczęśliwe zerwanie ; podczas gdy z B, musi szukać dłużej i może mieć więcej tekstu do zawinięcia (co może być kosztowne na niektórych terminalach, np. wypisywanie spacji, a następnie wypuszczanie spacji w celu zastąpienia pakowanych liter).

Ale to czysta spekulacja.


559
To właściwie poprawna odpowiedź! Dodanie spacji po Brozwiązaniu go.
— Kuba Spatny

261
Istnieje kilka odpowiedzi, które pochodzą z trudnych doświadczeń. TJ i ja (ponieważ jesteśmy przyjaciółmi) dorastaliśmy w czasach Apple'a] [i zx80 / ​​81. Wówczas nie było wbudowanego zawijania słów. Więc oboje skończyliśmy pisać własne - więcej niż raz. Te lekcje trzymają się ciebie, zostają spalone w twoim jaszczurkowym mózgu. Ale jeśli później oparłeś się na kodzie, kiedy słowo środowiska otacza wszystko lub robisz to ręcznie przed uruchomieniem, trudniej jest napotkać problemy z zawijaniem słów.
— JockM

315
Genialna dedukcja. Powinniśmy jednak uogólnić na podstawie tej lekcji i zawsze mierzyć wydajność z wyjściem albo wyeliminowanym, skierowanym do / dev / null (NUL w systemie Windows) lub przynajmniej do pliku. Wyświetlanie na jakiejkolwiek konsoli jest ogólnie bardzo drogim We / Wy i zawsze zniekształca taktowanie - nawet jeśli nie tak dramatycznie mylące jak to.
— Bob Kerns

37
@MrLister: System.out.printlnnie robi zawijania tekstu; chodziło tylko o zawijanie słów (i blokowanie, więc System.out.printlnmusiałem czekać).
— TJ Crowder

35
@Chris - właściwie będę argumentować, że nie wydrukowanie ich jest rozwiązaniem problemu uzyskania dokładnych czasów algorytmu. Za każdym razem, gdy drukujesz na konsoli (dowolnego rodzaju), wywołujesz wszelkie zewnętrzne przetwarzanie niezwiązane z tym, co testujesz. To błąd w twojej procedurze pomiarowej, czysty i prosty. Z drugiej strony, jeśli postrzegasz problem nie jako pomiar, ale rozumiesz rozbieżność, to tak, nie drukowanie jest sztuczką debugującą. Wszystko sprowadza się do tego, który problem próbujesz rozwiązać?
— Bob Kerns

209

Przeprowadziłem testy na Eclipse vs. Netbeans 8.0.2, oba z Javą w wersji 1.8; Użyłem System.nanoTime()do pomiarów.

Zaćmienie:

W obu przypadkach dostałem ten sam czas - około 1,564 sekundy .

Netbeans:

  • Używanie „#”: 1,536 sekund
  • Używanie „B”: 44,164 sekundy

Wygląda więc na to, że Netbeans ma słabą wydajność w drukowaniu na konsoli.

Po dalszych badaniach zdałem sobie sprawę, że problemem jest zawijanie wiersza maksymalnego bufora Netbeans (nie ogranicza się do System.out.printlnkomend), czego dowodem jest ten kod:

for (int i = 0; i < 1000; i++) {
    long t1 = System.nanoTime();
    System.out.print("BBB......BBB"); \\<-contain 1000 "B"
    long t2 = System.nanoTime();
    System.out.println(t2-t1);
    System.out.println("");
}

Wyniki czasu są mniejsze niż 1 milisekunda dla każdej iteracji, z wyjątkiem co piątej iteracji , gdy wynik czasu wynosi około 225 milisekund. Coś w stylu (w nanosekundach):

BBB...31744
BBB...31744
BBB...31744
BBB...31744
BBB...226365807
BBB...31744
BBB...31744
BBB...31744
BBB...31744
BBB...226365807
.
.
.

I tak dalej..

Podsumowanie:

  1. Eclipse działa doskonale z „B”
  2. Netbeans ma problem z zawijaniem wierszy, który można rozwiązać (ponieważ problem nie występuje w zaćmieniu) (bez dodawania spacji po B („B”)).

32
czy możesz rozwinąć swoje strategie badawcze, a następnie, co ostatecznie doprowadziło cię do odkrycia, że ​​winowajcą było owijanie linii? (To znaczy, jestem ciekaw twoich umiejętności detektywistycznych!)
— silph 30.10.16

12

Tak, winowajcą jest zdecydowanie zawijanie słów. Kiedy przetestowałem twoje dwa programy, NetBeans IDE 8.2 dał mi następujący wynik.

  1. Pierwsza matryca: O i # = 6,03 sekundy
  2. Druga macierz: O i B = 50,97 sekundy

Przyglądając się dokładnie kodowi, użyłeś podziału linii na końcu pierwszej pętli. Ale nie użyłeś żadnego podziału linii w drugiej pętli. Zamierzasz więc wydrukować słowo zawierające 1000 znaków w drugiej pętli. To powoduje problem z zawijaniem słów. Jeśli użyjemy nie-słownego znaku „” po B, skompilowanie programu zajmuje tylko 5,35 sekundy . A jeśli zastosujemy podział linii w drugiej pętli po przejściu 100 wartości lub 50 wartości, zajmie to odpowiednio tylko 8,56 sekundy i 7,05 sekundy .

Random r = new Random();
for (int i = 0; i < 1000; i++) {
    for (int j = 0; j < 1000; j++) {
        if(r.nextInt(4) == 0) {
            System.out.print("O");
        } else {
            System.out.print("B");
        }
        if(j%100==0){               //Adding a line break in second loop      
            System.out.println();
        }                    
    }
    System.out.println("");                
}

Kolejna rada to taka, aby zmienić ustawienia IDE NetBeans. Przede wszystkim przejdź do NetBeans Tools i kliknij Opcje . Następnie kliknij Edytor i przejdź do zakładki Formatowanie . Następnie wybierz Anywhere in Line Wrap Option. Skompilowanie programu zajmie prawie 6,24% mniej czasu.

Ustawienia edytora NetBeans

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.