벤치마킹(benchmarking)이란 측정의 기준이 되는 대상을 설정하고 그 대상과 비교 분석을 하는 행위로
JMH(Java Microbenchmark Harness)을 사용하면 자바에서 간단하게 벤치마킹을 하여 성능 비교를 할 수 있다.
[JMH 사용법]
Stack 자료구조를 공부하던 중 System.arraycopy()를 발견하고 Arrays.copy()랑 무슨 차이가 있나 알아보았다.
사실 Arrays.copyOf() 메서드가 어떻게 되어있나 한번 봤으면 내부적으로 System.arraycopy() 를 호출한다는 것을 바로 알았을텐데 멍청하게도 둘이 다른 것인줄 알았다.
System.arraycopy()의 경우 src의 지정된 위치 srcPos에서부터 dest의 지정된 위치 destPos로 length만큼 복사를 하는데 JVM은 복사하기 전 소스 배열과 대상 배열의 타입 비교를 한다.
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
Arrays.copyOf()는 내부에서 System.arraycopy()를 호출하는만큼 별도로 추가적인 기능을 제공하는데 새로운 배열을 만들고 내용을 자르거나 채울 수 있다. Object 타입 체크를 하는 arraycopy()와 달리 두 Object가 다른 타입일 경우에도 Arrays.newInstance()로 생성을 한다. (primitive 타입일 경우는 바로 배열을 생성하고 복사)
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
동적 배열 할당의 경우에는 배열을 동적으로 늘려야되기 때문에 Arrays.copyOf()를 사용하면 되지만 새로운 인스턴스를 생성하지 않고 원하는 부분만 부분적으로 복사하고 싶은 경우 System.arraycopy()를 직접 사용하는 것도 생각해보면 좋을 것 같다.
다음은 그래도 JMH를 간단하게라도 한번 사용해보고 싶어서 새로운 인스턴스를 생성하지 않는 경우 System.arraycopy()로 값을 복사할 때랑 Arrays.copyOf()를 사용해서 새로운 인스턴스가 계속 생성되는 경우 속도 차이를 비교해보았다.
@BenchmarkMode로 벤치마킹 모드를 설정하고 @Warmup, @Meansurement로 벤치마킹 전 워밍업 테스트를 몇 번 실행할지, 실제로 몇 번 측정할지 설정할 수 있다. @Fork로 벤치마킹 실행 횟수를 설정할 수 있고 그 외에도 다양한 설정들이 있다.
public class BenchmarkRunner {
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(args);
}
}
package benchmarking;
import org.openjdk.jmh.annotations.*;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5)
@Fork(value = 1)
@Measurement(iterations = 5)
public class PrimitivesCopyBenchmark {
@Param("10")
public int SIZE;
int[] src;
@Setup
public void setup() {
Random r = new Random();
src = new int[SIZE];
for (int i = 0; i < SIZE; i++) {
src[i] = r.nextInt();
}
}
@Benchmark
public int[] systemArrayCopyBenchmark() {
System.arraycopy(src, 0, src, 0, SIZE);
return src;
}
@Benchmark
public int[] arraysCopyOfBenchmark() {
return Arrays.copyOf(src, SIZE);
}
}
벤치마킹 결과
# Run progress: 0.00% complete, ETA 00:03:20
# Fork: 1 of 1
# Warmup Iteration 1: 9.806 ns/op
# Warmup Iteration 2: 8.583 ns/op
# Warmup Iteration 3: 7.721 ns/op
# Warmup Iteration 4: 7.707 ns/op
# Warmup Iteration 5: 7.693 ns/op
Iteration 1: 7.651 ns/op
Iteration 2: 7.810 ns/op
Iteration 3: 7.729 ns/op
Iteration 4: 7.824 ns/op
Iteration 5: 7.650 ns/op
Result "benchmarking.PrimitivesCopyBenchmark.arraysCopyOfBenchmark":
7.733 ±(99.9%) 0.322 ns/op [Average]
(min, avg, max) = (7.650, 7.733, 7.824), stdev = 0.084
CI (99.9%): [7.411, 8.054] (assumes normal distribution)
# Run progress: 50.00% complete, ETA 00:01:40
# Fork: 1 of 1
# Warmup Iteration 1: 6.442 ns/op
# Warmup Iteration 2: 5.592 ns/op
# Warmup Iteration 3: 5.658 ns/op
# Warmup Iteration 4: 5.667 ns/op
# Warmup Iteration 5: 6.035 ns/op
Iteration 1: 6.255 ns/op
Iteration 2: 6.291 ns/op
Iteration 3: 6.247 ns/op
Iteration 4: 6.034 ns/op
Iteration 5: 6.463 ns/op
Result "benchmarking.PrimitivesCopyBenchmark.systemArrayCopyBenchmark":
6.258 ±(99.9%) 0.589 ns/op [Average]
(min, avg, max) = (6.034, 6.258, 6.463), stdev = 0.153
CI (99.9%): [5.669, 6.847] (assumes normal distribution)
당연히 System.arraycopy()의 결과가 빠른 것을 볼 수 있다.
System.arraycopy() vs Arrays.copyOf()
IntelliJ에서 바로 실행하는 법
JMH gradle 설정
'Java' 카테고리의 다른 글
[Java] 제네릭에서 Raw Type 선언 주의 (이펙티브 자바) (0) | 2022.08.16 |
---|---|
[Java] HashMap put() 반환값 (null 조심) (0) | 2022.08.09 |
[Java] equals() 사용시 주의점 (NullPointerException) (0) | 2022.08.04 |
[Java] ENUM의 활용 ( (0) | 2022.07.22 |
[Java] 자바 예외 (Exception) (0) | 2022.06.19 |