여기에는 두 가지 '힘'이 있습니다. 성능과 가독성.
세 번째 문제를 먼저 다루겠습니다.
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");
vs.
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 회 반복 인쇄하는 데 걸리는 시간을 측정합니다.
내가 명령 프롬프트 유닉스 (리눅스)에서 실행하고, 리디렉션 경우 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
이것은 무엇을 의미 하는가? 마지막 '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
그것은 모든 의도와 목적을 위해, '긴'줄이 약 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
훨씬 느리지 만 비율은 거의 같습니다 ....