Qui ci sono due "forze", in tensione: prestazioni vs. leggibilità.
Affrontiamo prima il terzo problema, però: le linee lunghe:
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.");
Il modo migliore per implementare questo e mantenere la leggibilità è utilizzare la concatenazione delle stringhe:
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.");
La concatenazione di stringhe costanti avverrà in fase di compilazione e non avrà alcun effetto sulle prestazioni. Le linee sono leggibili e puoi semplicemente andare avanti.
Ora, riguardo a:
System.out.println("Good morning.");
System.out.println("Please enter your name");
vs.
System.out.println("Good morning.\nPlease enter your name");
La seconda opzione è significativamente più veloce. Ti suggerirò circa 2 volte più veloce .... perché?
Poiché il 90% (con un ampio margine di errore) del lavoro non è correlato al dumping dei caratteri nell'output, ma è necessario un sovraccarico per proteggere l'output in modo che scriva ad esso.
Sincronizzazione
System.out
è un PrintStream
. Tutte le implementazioni Java che conosco, sincronizzano internamente PrintStream: Vedi il codice su GrepCode! .
Che cosa significa questo per il tuo codice?
Significa che ogni volta che chiami System.out.println(...)
stai sincronizzando il tuo modello di memoria, stai controllando e aspettando un blocco. Anche eventuali altri thread che chiamano System.out verranno bloccati.
Nelle applicazioni a thread singolo l'impatto di System.out.println()
è spesso limitato dalle prestazioni IO del tuo sistema, quanto velocemente puoi scrivere su file. Nelle applicazioni multithread, il blocco può essere più un problema dell'IO.
Flushing
Ogni println viene svuotato . Ciò fa sì che i buffer vengano cancellati e attiva una scrittura a livello di console nei buffer. La quantità di sforzo svolto qui dipende dall'implementazione, ma è generalmente inteso che le prestazioni dello svuotamento sono solo in piccola parte correlate alla dimensione del buffer che viene svuotato. Esiste un sovraccarico significativo relativo allo svuotamento, in cui i buffer di memoria sono contrassegnati come sporchi, la macchina virtuale sta eseguendo l'I / O e così via. Incrementare il sovraccarico una volta, anziché due, è un'ovvia ottimizzazione.
Alcuni numeri
Ho messo insieme il seguente piccolo test:
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);
}
}
Il codice è relativamente semplice, stampa ripetutamente una stringa breve o lunga da emettere. La lunga stringa ha più righe nuove in essa. Misura il tempo necessario per stampare 1000 iterazioni di ciascuno.
Se lo eseguo al prompt dei comandi di Unix (Linux) e reindirizzo STDOUT
a /dev/null
e stampo i risultati effettivi su STDERR
, posso eseguire le seguenti operazioni:
java -cp . ConsolePerf > /dev/null 2> ../errlog
L'output (in errlog) assomiglia 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
Che cosa significa? Permettimi di ripetere l'ultima 'stanza':
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
Significa che, a tutti gli effetti, anche se la linea "lunga" è circa 5 volte più lunga e contiene più righe nuove, ci vuole un tempo di uscita pari a quella della linea corta.
Il numero di caratteri al secondo a lungo termine è 5 volte tanto, e il tempo trascorso è circa lo stesso .....
In altre parole, le tue prestazioni sono ridimensionate rispetto al numero di printlns che hai, non che loro stampano.
Aggiornamento: cosa succede se reindirizza a un file anziché a / 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
È molto più lento, ma le proporzioni sono quasi le stesse ...