Existem duas 'forças' aqui, em tensão: desempenho versus legibilidade.
Vamos abordar o terceiro problema primeiro, longas filas:
System.out.println("Good morning everyone. I am here today to present you with a very, very lengthy sentence in order to prove a point about how it looks strange amongst other code.");
A melhor maneira de implementar isso e manter a legibilidade é usar a concatenação de strings:
System.out.println("Good morning everyone. I am here today to present you "
+ "with a very, very lengthy sentence in order to prove a "
+ "point about how it looks strange amongst other code.");
A concatenação constante de String ocorrerá no tempo de compilação e não terá efeito sobre o desempenho. As linhas são legíveis e você pode simplesmente seguir em frente.
Agora, sobre o:
System.out.println("Good morning.");
System.out.println("Please enter your name");
vs.
System.out.println("Good morning.\nPlease enter your name");
A segunda opção é significativamente mais rápida. Vou sugerir cerca de 2X o mais rápido .... por quê?
Porque 90% (com uma ampla margem de erro) do trabalho não está relacionado ao descarte dos caracteres na saída, mas é uma sobrecarga necessária para garantir a saída da gravação.
Sincronização
System.out
é um PrintStream
. Todas as implementações de Java que eu conheço sincronizam internamente o PrintStream: Veja o código no GrepCode! .
O que isso significa para o seu código?
Isso significa que, toda vez que você liga System.out.println(...)
para sincronizar seu modelo de memória, você está verificando e aguardando um bloqueio. Quaisquer outros segmentos que chamam System.out também serão bloqueados.
Em aplicativos de thread único, o impacto System.out.println()
geralmente é limitado pelo desempenho de E / S do seu sistema, com que rapidez você pode gravar em um arquivo. Em aplicativos multithread, o bloqueio pode ser mais um problema do que o IO.
Flushing
Cada impressão é liberada . Isso faz com que os buffers sejam limpos e aciona uma gravação no nível do console nos buffers. A quantidade de esforço realizado aqui depende da implementação, mas, geralmente, entende-se que o desempenho da liberação está apenas em pequena parte relacionado ao tamanho do buffer que está sendo liberado. Há uma sobrecarga significativa relacionada à liberação, onde os buffers de memória são marcados como sujos, a máquina virtual está executando E / S e assim por diante. Incorrer nessa sobrecarga uma vez, em vez de duas, é uma otimização óbvia.
Alguns números
Eu montei o seguinte pequeno teste:
public class ConsolePerf {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
benchmark("Warm " + i);
}
benchmark("real");
}
private static void benchmark(String string) {
benchString(string + "short", "This is a short String");
benchString(string + "long", "This is a long String with a number of newlines\n"
+ "in it, that should simulate\n"
+ "printing some long sentences and log\n"
+ "messages.");
}
private static final int REPS = 1000;
private static void benchString(String name, String value) {
long time = System.nanoTime();
for (int i = 0; i < REPS; i++) {
System.out.println(value);
}
double ms = (System.nanoTime() - time) / 1000000.0;
System.err.printf("%s run in%n %12.3fms%n %12.3f lines per ms%n %12.3f chars per ms%n",
name, ms, REPS/ms, REPS * (value.length() + 1) / ms);
}
}
O código é relativamente simples, imprime repetidamente uma cadeia curta ou longa para a saída. A cadeia longa possui várias linhas novas. Ele mede quanto tempo leva para imprimir 1000 iterações de cada.
Se eu executá-lo no prompt de comando do unix (Linux), redirecionar o STDOUT
para /dev/null
e imprimir os resultados reais STDERR
, posso fazer o seguinte:
java -cp . ConsolePerf > /dev/null 2> ../errlog
A saída (no errlog) é semelhante a:
Warm 0short run in
7.264ms
137.667 lines per ms
3166.345 chars per ms
Warm 0long run in
1.661ms
602.051 lines per ms
74654.317 chars per ms
Warm 1short run in
1.615ms
619.327 lines per ms
14244.511 chars per ms
Warm 1long run in
2.524ms
396.238 lines per ms
49133.487 chars per ms
.......
Warm 99short run in
1.159ms
862.569 lines per ms
19839.079 chars per ms
Warm 99long run in
1.213ms
824.393 lines per ms
102224.706 chars per ms
realshort run in
1.204ms
830.520 lines per ms
19101.959 chars per ms
reallong run in
1.215ms
823.160 lines per ms
102071.811 chars per ms
O que isto significa? Deixe-me repetir a última estrofe:
realshort run in
1.204ms
830.520 lines per ms
19101.959 chars per ms
reallong run in
1.215ms
823.160 lines per ms
102071.811 chars per ms
Isso significa que, para todos os efeitos, mesmo que a linha 'longa' seja cerca de cinco vezes maior e contenha várias linhas novas, leva apenas o tempo necessário para produzir a linha curta.
O número de caracteres por segundo a longo prazo é 5 vezes maior e o tempo decorrido é aproximadamente o mesmo ...
Em outras palavras, seu desempenho é escalável em relação ao número de impressoras que você possui, não ao que elas imprimem.
Atualização: o que acontece se você redirecionar para um arquivo, em vez de para / dev / null?
realshort run in
2.592ms
385.815 lines per ms
8873.755 chars per ms
reallong run in
2.686ms
372.306 lines per ms
46165.955 chars per ms
É muito mais lento, mas as proporções são as mesmas ....