緊張状態には、パフォーマンスと読みやすさの2つの「力」があります。
最初に3番目の問題に取り組みましょう。長い行です。
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番目のオプションは大幅に高速です。2Xほど速く提案します。...なぜですか?
作業の90%(エラーのマージンが広い)は、文字を出力にダンプすることとは関係ありませんが、出力を書き込むために出力を保護するために必要なオーバーヘッドです。
同期
System.out
ですPrintStream
。私が知っているすべてのJava実装は、PrintStreamを内部的に同期します:GrepCodeのコードを参照してください!。
これはあなたのコードにとって何を意味しますか?
これは、呼び出すたびにSystem.out.println(...)
メモリモデルを同期し、ロックを確認して待機することを意味します。System.outを呼び出す他のスレッドもロックされます。
シングルスレッドアプリケーションでSystem.out.println()
は、多くの場合、システムのIOパフォーマンス、ファイルへの書き込み速度によって影響が制限されます。マルチスレッドアプリケーションでは、IOよりもロックが問題になることがあります。
フラッシング
各printlnはフラッシュされます。これにより、バッファーがクリアされ、バッファーへのコンソールレベルの書き込みがトリガーされます。ここで行われる労力は実装に依存しますが、フラッシュのパフォーマンスは、フラッシュされるバッファのサイズに関連する小さな部分にすぎないと一般に理解されています。メモリバッファがダーティとしてマークされ、仮想マシンがIOを実行しているなど、フラッシュに関連する大きなオーバーヘッドがあります。そのオーバーヘッドを2回ではなく1回発生させることは、明らかな最適化です。
いくつかの数字
次の小さなテストをまとめました。
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倍長く、複数の改行が含まれていても、出力には短い行とほぼ同じ時間がかかります。
長い目で見た場合の1秒あたりの文字数は5倍であり、経過時間はほぼ同じです.....
つまり、パフォーマンスは、印刷するものではなく、所有しているprintln の数に比例します。
更新: / 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
かなり遅いですが、比率はほぼ同じです。