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
Kuba Spatny
źródło
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.

TJ Crowder
źródło
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”)).
Roy Shmuli
źródło
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

Abdul Alim Shakir
źródło