¿Por qué la impresión "B" es dramáticamente más lenta que la impresión "#"?

2749

Genere dos matrices de 1000x 1000:

Primera matriz: Oy #.
Segunda matriz: Oy B.

Usando el siguiente código, la primera matriz tardó 8,52 segundos en completarse:

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

Con este código, la segunda matriz tardó 259.152 segundos en completarse:

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

¿Cuál es la razón detrás de los tiempos de ejecución dramáticamente diferentes?


Como se sugiere en los comentarios, la impresión solo System.out.print("#");toma 7.8871segundos, mientras que System.out.print("B");da still printing....

Como otros que señalaron que funciona para ellos normalmente, probé Ideone.com por ejemplo, y ambas piezas de código se ejecutan a la misma velocidad.

Condiciónes de la prueba:

  • Ejecuté esta prueba desde Netbeans 7.2 , con el resultado en su consola
  • Solía System.nanoTime()para mediciones
Kuba Spatny
fuente
6262
Intente cambiar rand.nextInt (4) == 0 a i <250 para eliminar el efecto del generador aleatorio. Es posible que se quede sin entropía que ralentiza la generación aleatoria
2014
3
Ambos parecen funcionar durante la misma cantidad de tiempo en mi máquina, ~ 4 segundos.
Sotirios Delimanolis
155
si usted está sugiriendo que la impresión de B lleva más tiempo que la impresión # .... ¿Por qué no intenta imprimir todos los hoteles, todos los # en lugar de confiar en la variable aleatoria R
Kakarotto
18
Según la respuesta aceptada, aparentemente no intentó ejecutarlo con la salida redirigida a un archivo o / dev / null.
Barmar
24
@fejese, Random () no es un rng criptográfico y, por lo tanto, no usa el grupo de entropía.
Divida

Respuestas:

4073

La especulación pura es que está utilizando un terminal que intenta hacer un ajuste de palabras en lugar de un ajuste de caracteres, y lo trata Bcomo un carácter de palabra pero #como un carácter que no es de palabra. Entonces, cuando llega al final de una línea y busca un lugar para romper la línea, ve un salto #casi inmediato y feliz allí; mientras que con el B, tiene que seguir buscando durante más tiempo y puede tener más texto para ajustar (lo que puede ser costoso en algunos terminales, por ejemplo, generar espacios de retroceso, luego generar espacios para sobrescribir las letras que se envuelven).

Pero eso es pura especulación.

TJ Crowder
fuente
560
Esta es realmente la respuesta correcta! Agregar un espacio después de que lo Bresuelva.
Kuba Spatny
261
Hay algunas respuestas que provienen de la experiencia aprendida. TJ y yo (ya que somos amigos) crecimos en los días de Apple] [y el zx80 / ​​81. No había construido en word wrap en ese entonces. Así que ambos terminamos escribiendo el nuestro, más de una vez. Y esas lecciones se quedan contigo, se queman en tu cerebro de lagarto. Pero si se inclinó hacia el código después de eso, cuando la palabra de su entorno envuelve todo, o lo hace manualmente antes del tiempo de ejecución, es más difícil encontrarse con los problemas con el ajuste de la palabra.
JockM
315
Brillante deducción. Pero deberíamos generalizar a partir de esta lección, y siempre medir el rendimiento con la salida eliminada, dirigida a / dev / null (NUL en Windows) o al menos a un archivo. La visualización en cualquier tipo de consola generalmente es una IO muy costosa y siempre distorsiona los tiempos, incluso si no es tan dramáticamente confuso como este.
Bob Kerns
37
@MrLister: System.out.printlnno hace el wordwrapping; Lo que estaba produciendo era el ajuste de palabras (y el bloqueo, así que System.out.printlntuve que esperar).
TJ Crowder
35
@ Chris - en realidad, argumentaré que no imprimirlos ES la solución, al problema de obtener tiempos precisos del algoritmo. Cada vez que imprime en una Consola (de cualquier tipo), está invocando todo tipo de procesamiento externo no relacionado con lo que está probando el rendimiento. Eso es un error en su procedimiento de medición, puro y simple. Por otro lado, si ve el problema no como una medida, sino entendiendo la discrepancia, entonces sí, no imprimir es un truco de depuración. Todo se reduce a, ¿qué problema estás tratando de resolver?
Bob Kerns
210

Realicé pruebas en Eclipse vs Netbeans 8.0.2, ambas con Java versión 1.8; Solía System.nanoTime()para las mediciones.

Eclipse:

Obtuve el mismo tiempo en ambos casos , alrededor de 1.564 segundos .

Netbeans:

  • Usando "#": 1.536 segundos
  • Usando "B": 44.164 segundos

Por lo tanto, parece que Netbeans tiene un mal rendimiento en la impresión a la consola.

Después de más investigación, me di cuenta de que el problema es el ajuste de línea del búfer máximo de Netbeans (no está restringido al System.out.printlncomando), demostrado por este código:

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

Los resultados de tiempo son menos de 1 milisegundo cada iteración, excepto cada quinta iteración , cuando el resultado de tiempo es de alrededor de 225 milisegundos. Algo así (en nanosegundos):

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

Y así..

Resumen:

  1. Eclipse funciona perfectamente con "B"
  2. Netbeans tiene un problema de ajuste de línea que se puede resolver (porque el problema no ocurre en eclipse) (sin agregar espacio después de B ("B")).
Roy Shmuli
fuente
32
¿Puedes dar más detalles sobre tus estrategias de investigación y luego lo que finalmente te llevó a descubrir que el ajuste de línea fue el culpable? (¡Tengo curiosidad por tus habilidades de detective, eso es!)
silph
12

Sí, el culpable es definitivamente envolver palabras. Cuando probé sus dos programas, NetBeans IDE 8.2 me dio el siguiente resultado.

  1. Primera matriz: O y # = 6.03 segundos
  2. Segunda matriz: O y B = 50.97 segundos

Mirando de cerca su código, ha utilizado un salto de línea al final del primer bucle. Pero no usaste ningún salto de línea en el segundo bucle. Entonces va a imprimir una palabra con 1000 caracteres en el segundo bucle. Eso causa un problema de ajuste de palabras. Si usamos un carácter que no es palabra "" después de B, se tarda solo 5.35 segundos en compilar el programa. Y si usamos un salto de línea en el segundo bucle después de pasar 100 valores o 50 valores, solo lleva 8.56 segundos y 7.05 segundos respectivamente.

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

Otro consejo es que cambie la configuración de NetBeans IDE. En primer lugar, vaya a Herramientas NetBeans y haga clic en Opciones . Después de eso, haga clic en Editor y vaya a la pestaña Formato . Luego, seleccione Cualquier lugar en la opción de ajuste de línea . Tomará casi 6.24% menos tiempo compilar el programa.

Configuración del editor de NetBeans

Abdul Alim Shakir
fuente