문자열이 있다고 가정하면 이 문자열에 대해 많은 루프 연결 작업을 수행할 것입니다. "+"를 사용하면 성능이 가장 낮아집니다. 그런데 이 성능이 얼마나 나쁜가요? StringBuffer, StringBuilder 또는 String.concat()도 성능 테스트에 넣으면 결과는 어떻게 될까요? 이 기사에서는 이러한 질문에 대한 답변을 제공합니다!
Per4j를 사용하여 성능을 계산할 것입니다. 이 도구는 최소 및 최대 소요 시간, 통계 기간의 표준 편차 등과 같은 완전한 성능 지표 세트를 제공할 수 있기 때문입니다. 테스트 코드에서는 정확한 표준편차 값을 얻기 위해 20개의 접합 "*"를 50,000회 테스트를 진행하겠습니다. 문자열을 연결하는 데 사용할 방법은 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
연결 연산자(+)
문자열 연결 메소드 concat(String str)
StringBuffer 추가 메소드 Append(String str)
StringBuilder 추가 메소드 Append(String str)
마지막으로 바이트코드를 살펴보고 이러한 메서드가 정확히 어떻게 실행되는지 살펴보겠습니다. 이제 클래스를 만드는 것부터 시작해 보겠습니다. 각 루프의 성능을 계산하려면 코드의 각 테스트 코드를 Per4J 라이브러리로 래핑해야 합니다. 먼저 반복 횟수를 정의합니다.
다음과 같이 코드 코드를 복사합니다.
개인 정적 최종 int OUTER_ITERATION=20;
개인 정적 최종 int INNER_ITERATION=50000;
다음으로 위의 4가지 방법을 사용하여 테스트 코드를 구현하겠습니다.
다음과 같이 코드 코드를 복사합니다.
문자열 addTestStr = "";
문자열 concatTestStr = "";
StringBuffer concatTestSb = null;
StringBuilder concatTestSbu = null;
for (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringAddConcat");
addTestStr = "";
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
addTestStr += "*";
stopWatch.stop();
}
for (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringConcat");
concatTestStr = "";
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestStr.concat("*");
stopWatch.stop();
}
for (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBufferConcat");
concatTestSb = 새로운 StringBuffer();
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSb.append("*");
stopWatch.stop();
}
for (int externalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBuilderConcat");
concatTestSbu = 새로운 StringBuilder();
for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSbu.append("*");
stopWatch.stop();
}
다음으로 프로그램을 실행하여 성능 지표를 생성합니다. 내 운영 환경은 64비트 Windows 7 운영 체제, 4GB 메모리와 듀얼 코어 Quad 2.00GHz CPU를 갖춘 32비트 JVM(7개) 시스템입니다.
우리가 상상했던 대로 완벽하게 나왔어요. 유일하게 흥미로운 점은 String.concat도 매우 좋은 이유입니다. 우리 모두 String이 상수 클래스(초기화 후에 변경되지 않는 클래스)라는 것을 알고 있는데, concat의 성능이 더 좋은 이유는 무엇일까요? (역자 주: 사실 원저자의 테스트 코드에 문제가 있습니다. concat() 메소드에 대한 테스트 코드는 concatTestStr=concatTestStr.concat(“*”) 와 같이 작성해야 합니다.) 이에 답하기 위해서는 질문, 우리는 concat 디컴파일을 살펴봐야 합니다. 바이트코드가 나옵니다. 이 기사의 다운로드 패키지에는 모든 바이트코드가 포함되어 있지만 이제 concat의 코드 조각을 살펴보겠습니다.
다음과 같이 코드 코드를 복사합니다.
46: 새로운 #6; //클래스 java/lang/StringBuilder
49: 듀프
50: Invokespecial #7; //메소드 java/lang/StringBuilder."<init>":()V
53: 로드_1
54: Invokevirtual #8; //메소드 java/lang/StringBuilder.append:
(Ljava/lang/String;)Ljava/lang/StringBuilder;
57: ldc #9;
59: Invokevirtual #8; //메소드 java/lang/StringBuilder.append:
(Ljava/lang/String;)Ljava/lang/StringBuilder;
62: Invokevirtual #10; //메소드 java/lang/StringBuilder.toString:()
L자바/랭/문자열;
65: astore_1
66: iinc 7, 1
69: 38로 이동
이 코드는 String.concat()의 바이트 코드입니다. 이 코드에서 concat() 메서드가 StringBuilder를 사용한다는 것을 분명히 알 수 있습니다. .append(str).append(str).toString() 작업을 수행하면 연결 성능에 영향을 미치므로 StringBuilder 및 String Cancate 시간은 1.8과 3.3입니다.
따라서 가장 간단한 연결을 수행할 때에도 StringBuffer 또는 StringBuilder 인스턴스를 생성하지 않으려면 concat을 사용해야 합니다. 그러나 문자열 접합 작업이 많은 경우에는 concat을 사용하면 안 됩니다. (번역자 참고 사항: 테스트 코드가 기능적으로 완전히 동일하지 않기 때문에 대체된 테스트 코드에서 concat의 평균 처리 시간은 1650.9밀리초입니다. 이 결과는 다음과 같습니다. 원본 텍스트 주석. ), concat은 프로그램 성능을 저하시키고 CPU를 소모하기 때문입니다. 따라서 스레드 안전성과 동기화를 고려하지 않고 최고의 성능을 얻으려면 StringBuilder를 사용해 보아야 합니다.