这里有两个“压力”,是紧张的:性能与可读性。
但是,让我们首先解决第三个问题,排长队:
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.");
做到这一点并保持可读性的最佳方法是使用字符串连接:
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.");
字符串常量连接将在编译时发生,并且对性能完全没有影响。线条清晰易读,您可以继续前进。
现在,关于:
System.out.println("Good morning.");
System.out.println("Please enter your name");
与
System.out.println("Good morning.\nPlease enter your name");
第二个选项明显更快。我会建议快2倍...。为什么?
因为90%的工作(有很大的误差)与将字符转储到输出无关,但与确保输出写入数据所需的开销有关。
同步化
System.out
是一个PrintStream
。我知道的所有Java实现都在内部同步PrintStream:请参见GrepCode上的代码!。
这对您的代码意味着什么?
这意味着,每次调用System.out.println(...)
同步存储模型时,您都在检查并等待锁定。任何其他调用System.out的线程也将被锁定。
在单线程应用程序中,其影响System.out.println()
通常受系统的IO性能,写入文件的速度限制。在多线程应用程序中,锁定比IO可能更多。
冲洗
每个println都被刷新。这将导致缓冲区被清除,并触发控制台级别的缓冲区写入。此处完成的工作量取决于实现方式,但是通常可以理解,刷新的性能仅在很小一部分上与要刷新的缓冲区的大小有关。与刷新相关的开销很大,内存刷新被标记为已脏,虚拟机正在执行IO,等等。一次优化的开销是一次而不是两次。
一些数字
我整理了以下小测试:
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);
}
}
该代码相对简单,它反复打印一个短字符串或长字符串以输出。长字符串中包含多个换行符。它测量每个打印1000次迭代所需的时间。
如果我在unix(Linux)命令提示符下运行它,并将其重定向STDOUT
到/dev/null
,并将实际结果打印到STDERR
,则可以执行以下操作:
java -cp . ConsolePerf > /dev/null 2> ../errlog
输出(在errlog中)如下所示:
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
这是什么意思?让我重复最后一个“节”:
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
这意味着,就所有意图和目的而言,即使“长”行的长度大约是5倍,并且包含多个换行符,其输出时间也仅与短线一样长。
从长远来看,每秒的字符数是5倍,并且经过的时间大约是相同的.....
换句话说,你的表现尺度相对数你有printlns的,没有什么他们打印。
更新:如果您重定向到文件而不是/ 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
它的速度要慢很多,但是比例大致相同。