Warum ist das Drucken von "B" dramatisch langsamer als das Drucken von "#"?

2746

Ich habe zwei Matrizen von 1000x erzeugt 1000:

Erste Matrix: Ound #.
Zweite Matrix: Ound B.

Unter Verwendung des folgenden Codes dauerte die Fertigstellung der ersten Matrix 8,52 Sekunden:

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("");
 }

Mit diesem Code dauerte die Fertigstellung der zweiten Matrix 259,152 Sekunden:

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("");
}

Was ist der Grund für die dramatisch unterschiedlichen Laufzeiten?


Wie in den Kommentaren vorgeschlagen, System.out.print("#");dauert das Drucken nur 7.8871Sekunden, während es System.out.print("B");gibt still printing....

Als andere, die darauf hinwiesen, dass es für sie normal funktioniert, habe ich zum Beispiel Ideone.com ausprobiert und beide Codeteile werden mit der gleichen Geschwindigkeit ausgeführt.

Test-Bedingungen:

  • Ich habe diesen Test von Netbeans 7.2 mit der Ausgabe in der Konsole ausgeführt
  • Ich habe System.nanoTime()für Messungen verwendet
Kuba Spatny
quelle
62
Versuchen Sie, rand.nextInt (4) == 0 in i <250 zu ändern, um den Effekt des Zufallsgenerators zu beseitigen. Sie könnten
keine
3
Beide scheinen auf meinem Computer für die gleiche Zeit zu laufen, ~ 4 Sekunden.
Sotirios Delimanolis
155
Wenn Sie vorschlagen, dass das Drucken von B länger dauert als das Drucken von # .... warum versuchen Sie nicht, alle B & all # zu drucken, anstatt sich auf die Zufallsvariable r zu verlassen
Kakarot
18
Basierend auf der akzeptierten Antwort haben Sie anscheinend nicht versucht, sie mit einer Ausgabe auszuführen, die in eine Datei oder / dev / null umgeleitet wurde.
Barmar
24
@fejese, Random () ist kein kryptografisches Rng und verbraucht daher den Entropiepool nicht.
Teilen Sie den

Antworten:

4070

Reine Spekulation ist, dass Sie ein Terminal verwenden, das versucht, Wortumbruch statt Zeichenumbruch durchzuführen, und das Bals Wortzeichen, aber #als Nichtwortzeichen behandelt wird . Wenn es also das Ende einer Linie erreicht und nach einem Ort sucht, an dem die Linie unterbrochen werden kann, sieht es eine #fast sofortige und glückliche Unterbrechung. Während mit dem Bmuss es länger suchen und muss möglicherweise mehr Text umbrechen (was an einigen Terminals teuer sein kann, z. B. das Ausgeben von Leerzeichen und das anschließende Ausgeben von Leerzeichen zum Überschreiben der umgebrachten Buchstaben).

Aber das ist reine Spekulation.

TJ Crowder
quelle
560
Das ist eigentlich die richtige Antwort! Hinzufügen eines Leerzeichens nach dem BLösen.
Kuba Spatny
261
Es gibt einige Antworten, die aus hart erlernten Erfahrungen stammen. TJ und ich (da wir Freunde sind) sind in den Tagen des Apple aufgewachsen] [und des zx80 / ​​81. Damals gab es keinen eingebauten Zeilenumbruch. Also haben wir beide unsere eigenen geschrieben - mehr als einmal. Und diese Lektionen bleiben bei dir, sie werden in dein Eidechsenhirn eingebrannt. Wenn Sie sich danach jedoch auf Code stützen, wenn Ihr Umgebungswort alles umschließt oder Sie dies vor der Laufzeit manuell tun, ist es schwieriger, auf die Probleme mit dem Zeilenumbruch zu stoßen.
JockM
315
Genialer Abzug. Wir sollten diese Lektion jedoch verallgemeinern und die Leistung immer messen, wobei die Ausgabe entweder eliminiert, an / dev / null (NUL unter Windows) oder zumindest an eine Datei gerichtet ist. Die Anzeige auf jeder Art von Konsole ist im Allgemeinen sehr teuer und verzerrt immer das Timing - auch wenn dies nicht so dramatisch verwirrend ist.
Bob Kerns
37
@ MrLister: System.out.printlnführt keine Wortumhüllung durch; Das, worauf es ausgegeben wurde, war das Umbrechen von Wörtern (und das Blockieren, also System.out.printlnmusste ich warten).
TJ Crowder
35
@ Chris - eigentlich werde ich argumentieren, dass das Nicht-Drucken die Lösung für das Problem ist, genaue Timings des Algorithmus zu erhalten. Jedes Mal, wenn Sie auf einer Konsole (jeglicher Art) drucken, rufen Sie alle Arten der externen Verarbeitung auf, die nicht mit dem zusammenhängen, wofür Sie die Leistung testen. Das ist schlicht und einfach ein Fehler in Ihrem Messverfahren. Wenn Sie das Problem jedoch nicht als Messung betrachten, sondern die Diskrepanz verstehen, ist das Nicht-Drucken ein Debugging-Trick. Es kommt darauf an, welches Problem Sie lösen wollen.
Bob Kerns
209

Ich habe Tests mit Eclipse vs Netbeans 8.0.2 durchgeführt, beide mit Java Version 1.8; Ich habe System.nanoTime()für Messungen verwendet.

Finsternis:

In beiden Fällen hatte ich die gleiche Zeit - ungefähr 1,564 Sekunden .

Netbeans:

  • Verwenden von "#": 1,536 Sekunden
  • Mit "B": 44,164 Sekunden

Es sieht also so aus, als ob Netbeans beim Drucken auf der Konsole eine schlechte Leistung aufweist.

Nach weiteren Recherchen wurde mir klar, dass das Problem das Zeilenumbruch des maximalen Puffers von Netbeans ist (es ist nicht auf System.out.printlnBefehle beschränkt), was durch diesen Code demonstriert wird:

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("");
}

Die Zeitergebnisse betragen weniger als 1 Millisekunde pro Iteration, außer bei jeder fünften Iteration , wenn das Zeitergebnis etwa 225 Millisekunden beträgt. So etwas wie (in Nanosekunden):

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

Und so weiter..

Zusammenfassung:

  1. Eclipse funktioniert perfekt mit "B"
  2. Netbeans hat ein Zeilenumbruchproblem, das gelöst werden kann (da das Problem bei Eclipse nicht auftritt) (ohne Leerzeichen nach B ("B")).
Roy Shmuli
quelle
32
Können Sie Ihre Forschungsstrategien näher erläutern und was hat Sie schließlich dazu gebracht, herauszufinden, dass das Zeilenumwickeln der Schuldige war? (Ich bin neugierig auf Ihre Detektivfähigkeiten, das heißt!)
Silph
12

Ja, der Täter ist definitiv ein Wortwickler. Als ich Ihre beiden Programme getestet habe, hat mir NetBeans IDE 8.2 das folgende Ergebnis geliefert.

  1. Erste Matrix: O und # = 6,03 Sekunden
  2. Zweite Matrix: O und B = 50,97 Sekunden

Wenn Sie sich Ihren Code genau ansehen, haben Sie am Ende der ersten Schleife einen Zeilenumbruch verwendet. In der zweiten Schleife haben Sie jedoch keinen Zeilenumbruch verwendet. Sie werden also in der zweiten Schleife ein Wort mit 1000 Zeichen drucken. Dies führt zu einem Problem mit dem Zeilenumbruch. Wenn wir nach B ein Nicht-Wort-Zeichen "" verwenden, dauert das Kompilieren des Programms nur 5,35 Sekunden . Und wenn wir in der zweiten Schleife nach Übergabe von 100 oder 50 Werten einen Zeilenumbruch verwenden, dauert dies nur 8,56 Sekunden bzw. 7,05 Sekunden .

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("");                
}

Ein weiterer Rat ist, die Einstellungen der NetBeans-IDE zu ändern. Gehen Sie zunächst zu NetBeans Tools und klicken Sie auf Optionen . Klicken Sie anschließend auf Editor und wechseln Sie zur Registerkarte Formatierung . Wählen Sie dann Anywhere in Line Wrap Option. Das Kompilieren des Programms dauert fast 6,24% weniger.

NetBeans Editor-Einstellungen

Abdul Alim Shakir
quelle