Java에서 올바른 마이크로 벤치 마크를 작성하는 방법


870

Java에서 올바른 마이크로 벤치 마크를 작성하고 실행하는 방법은 무엇입니까?

생각할 다양한 것들을 보여주는 코드 샘플과 주석을 찾고 있습니다.

예 : 벤치 마크가 시간 / 반복 또는 반복 / 시간을 측정해야하는 이유는 무엇입니까?

관련 : 스톱워치 벤치마킹이 허용됩니까?


관련 정보는 몇 분 전에 [이 질문] [1]을 참조하십시오. 편집 : 죄송합니다, 이것은 대답이 아닙니다. 의견으로 게시해야합니다. [1] : stackoverflow.com/questions/503877/…
Tiago

그 질문의 포스터를 이와 같은 질문에 언급하려고 계획 한 후에이 질문이 존재하지 않는다는 것을 언급했습니다. 여기에 시간이 지남에 따라 좋은 팁을 모을 수 있기를 바랍니다.
John Nilsson

5
Java 9는 마이크로 벤치마킹을위한 몇 가지 기능을 제공 할 수 있습니다. openjdk.java.net/jeps/230
Raedwald

1
@Raedwald 나는 JEP가 JDK 코드에 마이크로 벤치 마크를 추가하는 것을 목표로하고 있다고 생각하지만, jmh가 JDK에 포함될 것이라고는 생각하지 않습니다.
assylias

1
@Raedwald 미래에서 안녕하세요. 컷을하지 않았다 .
Michael

답변:


787

Java HotSpot 제작자의 마이크로 벤치 마크 작성에 대한 팁 :

규칙 0 : JVM 및 마이크로 벤치마킹에 대한 평판이 좋은 논문을 읽으십시오. 좋은 것은 Brian Goetz, 2005 입니다. 마이크로 벤치 마크에서 너무 많이 기대하지 마십시오. 제한된 범위의 JVM 성능 특성 만 측정합니다.

규칙 1 : 타이밍 단계 전에 모든 초기화 및 컴파일을 트리거 할 수있을만큼 테스트 커널을 완전히 실행하는 워밍업 단계를 항상 포함하십시오. 예열 단계에서는 반복 횟수가 적습니다. 일반적으로 수만 개의 내부 루프 반복이 있습니다.

규칙 2 : 항상 실행 -XX:+PrintCompilation, -verbose:gc당신은 컴파일러와 JVM의 다른 부분은 타이밍 단계에서 예상치 못한 일을하지 않는 것을 확인할 수 있도록, 등.

규칙 2.1 : 타이밍 및 워밍업 단계의 시작과 끝에 메시지를 인쇄하여 타이밍 단계 중에 규칙 2의 출력이 없는지 확인할 수 있습니다.

규칙 3 :-client 및 의 차이점 -server과 OSR 및 정기적 인 편집에 유의하십시오. 이 -XX:+PrintCompilation플래그는 초기 값이 아닌 진입 점을 표시하기 위해 at 기호로 OSR 컴파일을보고합니다 (예 :) Trouble$1::run @ 2 (41 bytes). 최상의 성능을 갖춘 경우 서버를 클라이언트로, OSR을 정기적으로 선호하십시오.

규칙 4 : 초기화 효과에 유의하십시오. 인쇄는 클래스를로드하고 초기화하기 때문에 타이밍 단계에서 처음으로 인쇄하지 마십시오. 클래스 로딩을 구체적으로 테스트하지 않는 한 (그리고이 경우 테스트 클래스 만로드) 워밍업 단계 (또는 최종보고 단계) 외부에 새 클래스를로드하지 마십시오. 규칙 2는 이러한 영향에 대한 첫 번째 방어선입니다.

규칙 5 : 최적화 해제 및 재 컴파일 효과에 유의하십시오. 경로가 전혀 사용되지 않을 것이라는 이전의 낙관적 인 가정에 따라 컴파일러가 코드를 정크 및 재 컴파일 할 수 있으므로 타이밍 단계에서 처음으로 코드 경로를 사용하지 마십시오. 규칙 2는 이러한 영향에 대한 첫 번째 방어선입니다.

규칙 6 : 적절한 도구를 사용하여 컴파일러의 마음을 읽고, 생성하는 코드에 놀라게 될 것입니다. 무언가를 더 빠르거나 느리게 만드는 것에 대한 이론을 만들기 전에 코드를 직접 검사하십시오.

규칙 7 : 측정시 노이즈를 줄입니다. 조용한 컴퓨터에서 벤치 마크를 실행하고 여러 번 실행하여 특이 치를 버립니다. -Xbatch컴파일러를 응용 프로그램과 직렬화하는 데 사용 하고 컴파일러가 -XX:CICompilerCount=1병렬로 실행되지 않도록 설정 하는 것을 고려 하십시오. GC 오버 헤드를 줄이고 최대한 Xmx(충분히) 동일하게 설정 Xms하고 가능한 UseEpsilonGC경우 사용하도록 최선을 다하십시오 .

규칙 8 : 벤치 마크에 라이브러리를 사용하는 것이 더 효율적일 수 있으며이 목적을 위해 이미 디버깅 되었기 때문입니다. 같은 JMH , 캘리퍼스 또는 빌과 자바에 대한 바울의 우수한 UCSD 벤치 마크 .


5
이 또한 흥미로운 기사였다 ibm.com/developerworks/java/library/j-jtp12214
존 닐슨

142
또한 정확도가 + 또는 -15ms 인 경우가 아니면 System.currentTimeMillis ()를 사용하지 마십시오. 이는 대부분의 OS + JVM 조합에서 일반적입니다. 대신 System.nanoTime ()을 사용하십시오.
Scott Carey


93
주목해야한다 System.nanoTime()되지 보장 보다 더 정확하다고 System.currentTimeMillis(). 최소한 정확해야합니다. 그러나 일반적으로 훨씬 더 정확합니다.
중력

41
System.nanoTime()대신에 사용해야하는 주된 이유 System.currentTimeMillis()는 전자가 단조 증가하는 것입니다. 리턴 된 값을 빼면 두 개의 currentTimeMillis호출이 실제로 NTP 디먼에 의해 시스템 시간이 조정 되었기 때문에 부정적인 결과를 낳을 수 있습니다.
Waldheinz

239

이 질문에 대한 답변이 표시되었지만 마이크로 벤치 마크 작성에 도움이되는 두 개의 라이브러리를 언급하고 싶습니다.

Google의 캘리퍼스

튜토리얼 시작하기

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

OpenJDK의 JMH

튜토리얼 시작하기

  1. JVM에서 벤치마킹 함정 방지
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://java-performance.info/jmh/

37
+1 승인 된 답변의 규칙 8로 추가되었을 수 있습니다. 규칙 8 : 너무 많은 일이 잘못 될 수 있으므로 직접 시도하지 말고 기존 라이브러리를 사용해야합니다!
assylias

8
: @Pangea JMH 요즘, 참조 캘리퍼스 아마 우수 groups.google.com/forum/#!msg/mechanical-sympathy/m4opvy4xq3U/...
assylias

86

Java 벤치 마크의 중요한 사항은 다음과 같습니다.

  • 코드 를 타이밍하기 전에 코드를 여러 번 실행하여 JIT를 먼저 예열 하십시오.
  • 결과를 초 또는 (더 나은) 수십 초로 측정 할 수있을 정도로 오래 실행하십시오
  • System.gc()반복 사이에 호출 할 수는 없지만 테스트간에 실행하는 것이 좋습니다. 따라서 각 테스트에서 "깨끗한"메모리 공간을 확보 할 수 있습니다. (예, gc()보증보다 힌트가 더 많지만 실제로 내 경험에서 가비지 수집 가능성큽니다 .)
  • 반복 및 시간을 표시하고 "최상의"알고리즘이 1.0의 점수를 얻고 다른 알고리즘이 상대적으로 점수가 매겨 질 수 있도록 시간 / 반복 점수를 표시하고 싶습니다. 즉 , 반복 횟수와 시간을 모두 바꾸면서도 모든 알고리즘을 오랫동안 실행할 수 있지만 여전히 비슷한 결과를 얻을 수 있습니다.

.NET에서 벤치마킹 프레임 워크 디자인에 대해 블로그하는 중입니다. 나는 당신에게 몇 가지 아이디어를 줄 수 있는 가지 초기 게시물을 가지고 있습니다. 물론 모든 것이 적절하지는 않지만 일부는 적합 할 수도 있습니다.


3
작은 nitpick : IMO는 "각 테스트를 받도록" "각 테스트를 받도록"해야합니다. 전자는 호출이 gc 항상 사용되지 않은 메모리를 해제 한다는 인상을주기 때문 입니다.
Sanjay T. Sharma

@ SanjayT.Sharma : 글쎄, 의도 는 실제로 그렇게한다는 것입니다. 엄격하게 보장되지는 않지만 실제로는 매우 강력한 힌트입니다. 더 명확하게 편집합니다.
Jon Skeet

1
System.gc () 호출에 동의하지 않습니다. 그게 힌트입니다. "희망적으로 무언가를 할 것"조차 아닙니다. 절대 전화해서는 안됩니다. 이것은 예술이 아니라 프로그래밍입니다.
gyorgyabraham 2016 년

13
@ gyabraham : 그렇습니다. 힌트입니다. 그러나 그것은 일반적으로 찍은 것으로 관찰 된 것입니다. 따라서을 사용하지 않으 System.gc()려면 이전 테스트에서 만든 객체로 인해 한 테스트에서 가비지 수집을 최소화하도록 제안하는 방법은 무엇입니까? 나는 독단적이 아닌 실용적입니다.
Jon Skeet 2016 년

9
@ gyabraham : 나는 당신이 "큰 대체"의 의미를 모른다. 좀 더 정교하고 더 나은 결과를 줄 수있는 제안이 있습니까? 나는 그것이 보증이 아니라고 분명히 말
했었다

48

jmh 는 OpenJDK에 최근 추가 된 것으로 Oracle의 일부 성능 엔지니어가 작성했습니다. 확실히 볼만한 가치가 있습니다.

jmh는 Java 및 JVM을 대상으로하는 다른 언어로 작성된 나노 / 마이크로 / 매크로 벤치 마크를 작성, 실행 및 분석하기위한 Java 하네스입니다.

샘플 테스트 주석에 묻힌 매우 흥미로운 정보 조각 .

또한보십시오:


1
JMH 시작에 대한 자세한 내용은이 블로그 게시물 ( psy-lob-saw.blogspot.com/2013/04/…) 을 참조하십시오.
Nitsan Wakart

참고로, JEP 230 : Microbenchmark Suite 는 이 JMH (Java Microbenchmark Harness) 프로젝트를 기반으로 하는 OpenJDK 제안 입니다. Java 9를 자르지 않았지만 나중에 추가 할 수 있습니다.
Basil Bourque

23

벤치 마크가 시간 / 반복 또는 반복 / 시간을 측정해야하는 이유는 무엇입니까?

이에 따라 무엇을 당신이 시험에 노력하고 있습니다.

대기 시간 에 관심이있는 경우 시간 / 반복을 사용 하고 처리량에 관심이있는 경우 반복 / 시간을 사용하십시오.


16

두 알고리즘을 비교하려는 경우 순서에 따라 두 개 이상의 벤치 마크를 수행하십시오. 즉 :

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

다른 패스에서 동일한 알고리즘의 런타임에서 눈에 띄는 차이 (때로는 5-10 %)를 발견했습니다.

또한 각 루프의 런타임이 최소 10 초 정도가되도록 n 이 매우 큰지 확인하십시오 . 반복 횟수가 많을수록 벤치 마크 시간이 더 중요하고 데이터의 안정성이 높아집니다.


5
자연스럽게 순서를 변경하면 런타임에 영향을줍니다. JVM 최적화 및 캐싱 효과가 여기에서 작동합니다. 더 나은 방법은 JVM 최적화를 '따뜻하게'여러 번 실행하고 다른 JVM에서 모든 테스트를 벤치 마크하는 것입니다.
Mnementh

15

어떻게 든 벤치마킹 된 코드로 계산 된 결과를 사용해야합니다. 그렇지 않으면 코드를 최적화 할 수 있습니다.


13

Java로 마이크로 벤치 마크를 작성하는 데는 많은 함정이 있습니다.

첫째 : 가비지 수집, 캐싱 효과 (파일의 OS 및 메모리의 CPU), IO 등 시간이 다소 무작위로 걸리는 모든 종류의 이벤트를 계산해야합니다.

둘째 : 매우 짧은 간격 동안 측정 된 시간의 정확도를 신뢰할 수 없습니다.

셋째 : JVM은 실행하는 동안 코드를 최적화합니다. 따라서 동일한 JVM 인스턴스에서 다른 실행이 더 빨라집니다.

내 권장 사항 : 벤치 마크를 몇 초 동안 실행하십시오. 즉, 밀리 초 이상의 런타임보다 안정적입니다. JVM을 준비합니다 (측정하지 않고 벤치 마크를 한 번 이상 실행하면 JVM이 최적화를 실행할 수 있음). 그리고 벤치 마크를 여러 번 (아마도 5 회) 실행하고 중앙값을 가져옵니다. 새로운 JVM 인스턴스에서 모든 마이크로 벤치 마크를 실행하십시오 (모든 벤치 마크 새 Java를 호출). 그렇지 않으면 JVM의 최적화 효과가 나중에 실행중인 테스트에 영향을 줄 수 있습니다. 예열 단계에서 실행되지 않는 작업은 실행하지 마십시오 (클래스로드 및 재 컴파일을 트리거 할 수 있음).


8

또한 다른 구현을 비교할 때 마이크로 벤치 마크 결과를 분석하는 것이 중요 할 수도 있습니다. 따라서 유의성 테스트를 수행 해야합니다.

이는 A대부분의 벤치 마크 실행 중에 구현 이 구현 보다 빠를 수 있기 때문 B입니다. 그러나 A스프레드가 더 높을 수 있으므로 측정 성능의 이점은와 A비교할 때 아무런 의미가 없습니다 B.

따라서 마이크로 벤치 마크를 정확하게 작성하고 실행하는 것뿐만 아니라 올바르게 분석하는 것도 중요합니다.


8

다른 훌륭한 조언을 추가하기 위해 다음 사항도 염두에 두어야합니다.

일부 CPU (예 : TurboBoost가 포함 된 Intel Core i5 범위)의 경우 온도 (및 현재 사용중인 코어 수 및 전체 활용률)가 ​​클럭 속도에 영향을줍니다. CPU는 동적으로 클럭되므로 결과에 영향을 줄 수 있습니다. 예를 들어 단일 스레드 응용 프로그램을 사용하는 경우 최대 코어 속도 (TurboBoost 사용)는 모든 코어를 사용하는 응용 프로그램보다 높습니다. 따라서 일부 시스템에서 단일 및 다중 스레드 성능의 비교를 방해 할 수 있습니다. 온도와 전압은 터보 주파수가 유지되는 시간에도 영향을 미칩니다.

아마도 직접적으로 제어 할 수있는보다 근본적으로 중요한 측면 일 것입니다. 올바른 것을 측정하고 있는지 확인하십시오! 예를 들어, System.nanoTime()특정 코드 비트를 벤치마킹하는 데 사용 하는 경우 관심없는 항목을 측정하지 않으려면 적절한 위치에 할당을 호출하십시오. 예를 들어,하지 마십시오.

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

문제는 코드가 끝나면 즉시 종료 시간을 얻지 못한다는 것입니다. 대신 다음을 시도하십시오.

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");

예, 시간이 지정된 지역 내에서 관련없는 작업을 수행하지 않는 것이 중요하지만 첫 번째 예는 여전히 좋습니다. println별도의 헤더 행이 아닌에 대한 호출이 단 하나 뿐이며 해당 호출에 대한 문자열 인수를 구성하는 첫 번째 단계 System.nanoTime()로 평가 되어야합니다. 컴파일러가 첫 번째로 할 수있는 것은 없으며 두 번째로는 할 수 없으며 정지 시간을 기록하기 전에 추가 작업을 수행하도록 권장하는 사람도 없습니다.
Peter Cordes

7

http://opt.sourceforge.net/ Java Micro Benchmark-다른 플랫폼에서 컴퓨터 시스템의 비교 성능 특성을 결정하는 데 필요한 제어 작업. 최적화 결정을 안내하고 다른 Java 구현을 비교하는 데 사용할 수 있습니다.


2
임의의 Java 코드가 아닌 JVM + 하드웨어를 벤치마킹하는 것 같습니다.
Stefan L
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.