Por que imprimir "B" é muito mais lento que imprimir "#"?

2749

Eu gerei duas matrizes de 1000x 1000:

Primeira matriz: Oe #.
Segunda matriz: Oe B.

Usando o código a seguir, a primeira matriz levou 8,52 segundos para ser concluída:

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

Com esse código, a segunda matriz levou 259.152 segundos para concluir:

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

Qual é a razão por trás dos tempos de execução dramaticamente diferentes?


Como sugerido nos comentários, a impressão System.out.print("#");leva apenas 7.8871alguns segundos, enquanto a System.out.print("B");doação still printing....

Como outros que apontaram que isso funciona normalmente para eles, tentei o Ideone.com por exemplo, e os dois pedaços de código são executados na mesma velocidade.

Condições de teste:

  • Fiz esse teste no Netbeans 7.2 , com a saída no console
  • Eu usei System.nanoTime()para medições
Kuba Spatny
fonte
62
Tente alterar rand.nextInt (4) == 0 para i <250 para eliminar o efeito do gerador aleatório. Você pode correr para fora da entropia que retarda a geração aleatória
fejese
3
Ambos parecem funcionar pela mesma quantidade de tempo na minha máquina, ~ 4 segundos.
Sotirios Delimanolis
155
Se você está sugerindo que a impressão B leva mais tempo do que a impressão # .... por que você não tente imprimir todos B & all # em vez de depender variável r aleatório
Kakarot
18
Com base na resposta aceita, você aparentemente não tentou executá-lo com saída redirecionada para um arquivo ou / dev / null.
Barmar
24
@fejese, Random () não é um rng criptográfico e, portanto, não usa o pool de entropia.
Dividir

Respostas:

4073

Pura especulação é que você está usando um terminal que tenta fazer quebra de linha em vez de quebra de caractere e trata Bcomo um caractere de palavra, mas #como um caractere que não é palavra. Então, quando chega ao final de uma linha e procura um lugar para quebrá-la, vê uma #quebra quase imediata e feliz; enquanto que com o B, ele deve continuar pesquisando por mais tempo e pode ter mais texto para quebrar (o que pode ser caro em alguns terminais, por exemplo, na saída de backspaces, em seguida, na saída de espaços para substituir as letras que estão sendo quebradas).

Mas isso é pura especulação.

TJ Crowder
fonte
560
Esta é realmente a resposta correta! Adicionando um espaço após a Bresolução.
Kubat Spatny
261
Existem algumas respostas que vêm de experiências difíceis de aprender. TJ e eu (como somos amigos) crescemos nos dias da Apple] [e da zx80 / ​​81. Não havia quebra de linha incorporada naquela época. Então nós dois acabamos escrevendo os nossos - mais de uma vez. E essas lições ficam com você, elas são queimadas no seu cérebro de lagarto. Mas se você preferir codificar depois disso, quando a palavra do ambiente agrupar tudo, ou você fizer isso manualmente antes do tempo de execução, será mais difícil encontrar os problemas com quebra de linha.
23414 JockM
315
Dedução brilhante. Mas devemos generalizar a partir desta lição e sempre medir o desempenho com a saída eliminada, direcionada para / dev / null (NUL no Windows) ou pelo menos para um arquivo. A exibição em qualquer tipo de console geralmente é de E / S muito cara e sempre distorce os tempos - mesmo que não seja tão dramaticamente confuso quanto isso.
Bob Kerns
37
@ MrLister: System.out.printlnnão faz quebra de palavras; o que ele estava produzindo era a quebra de palavras (e o bloqueio, então System.out.printlntivemos que esperar).
TJ Crowder
35
@ Chris - na verdade, vou argumentar que não imprimi-los é a solução, para o problema de obter horários precisos do algoritmo. Cada vez que você imprime em um console (de qualquer tipo), invoca todo tipo de processamento externo não relacionado ao que está testando o desempenho. Isso é um bug no seu procedimento de medição, puro e simples. Por outro lado, se você vê o problema não como medida, mas entendendo a discrepância, sim, não imprimir é um truque de depuração. Tudo se resume a, qual problema você está tentando resolver?
Bob Kerns #
210

Eu realizei testes no Eclipse vs Netbeans 8.0.2, ambos com Java versão 1.8; Eu usei System.nanoTime()para medições.

Eclipse:

Eu tive o mesmo tempo nos dois casos - em torno de 1.564 segundos .

Netbeans:

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

Portanto, parece que o Netbeans tem um desempenho ruim na impressão para o console.

Após mais pesquisas, percebi que o problema é quebra de linha do buffer máximo do Netbeans (não está restrito ao System.out.printlncomando), demonstrado 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("");
}

Os resultados de tempo são inferiores a 1 milissegundo a cada iteração, exceto a cada quinta iteração , quando o resultado é de 225 milissegundos. Algo como (em nanossegundos):

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

E assim por diante..

Resumo:

  1. Eclipse funciona perfeitamente com "B"
  2. O Netbeans tem um problema de quebra de linha que pode ser resolvido (porque o problema não ocorre no eclipse) (sem adicionar espaço após B ("B")).
Roy Shmuli
fonte
32
você pode elaborar suas estratégias de pesquisa e o que finalmente o levou a descobrir que a quebra de linha era a culpada? (Estou curioso sobre suas habilidades de detetive, que é!)
Silph
12

Sim, o culpado é definitivamente quebra-cabeças. Quando testei seus dois programas, o NetBeans IDE 8.2 me deu o seguinte resultado.

  1. Primeira matriz: O e # = 6,03 segundos
  2. Segunda matriz: O e B = 50,97 segundos

Observando seu código de perto, você usou uma quebra de linha no final do primeiro loop. Mas você não usou nenhuma quebra de linha no segundo loop. Então, você imprimirá uma palavra com 1.000 caracteres no segundo loop. Isso causa um problema de quebra de linha. Se usarmos um caractere que não seja "" palavra após B, leva apenas 5,35 segundos para compilar o programa. E se usarmos uma quebra de linha no segundo loop após passar 100 valores ou 50 valores, leva apenas 8,56 segundos e 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("");                
}

Outro conselho é que, para alterar as configurações do NetBeans IDE. Primeiro de tudo, vá para Ferramentas NetBeans e clique em Opções . Depois disso, clique em Editor e vá para a guia Formatação . Em seguida, selecione Anywhere in Line Wrap Option. Levará quase 6,24% menos tempo para compilar o programa.

Configurações do editor NetBeans

Abdul Alim Shakir
fonte