벤치마킹(benchmarking)이란 측정의 기준이 되는 대상을 설정하고 그 대상과 비교 분석을 하는 행위로

JMH(Java Microbenchmark Harness)을 사용하면 자바에서 간단하게 벤치마킹을 하여 성능 비교를 할 수 있다.

 

[JMH 사용법]

 

Microbenchmarking with Java | Baeldung

Learn about JMH, the Java Microbenchmark Harness.

www.baeldung.com

 

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()

 

Performance of System.arraycopy() vs. Arrays.copyOf() | Baeldung

Learn about the implementation and performance of System.arraycopy() and Arrays.copyOf()

www.baeldung.com

IntelliJ에서 바로 실행하는 법

 

Scala: Unable to find the resource: /META-INF/BenchmarkList · Issue #13 · artyushov/idea-jmh-plugin

idea ultimate 14.1.2 + scala plugin 1.4.15 + jmh plugin 1.0.1 I have an sbt project opened in IDEA. When I run one benchmark method or all methods in a class I get this error: Exception in thread &...

github.com

JMH gradle 설정

 

JMH를 사용한 gradle 환경에서의 Java 코드 벤치마킹

몇 달전에 “코드 수행 시간을 어떻게 측정하나요?”를 포스팅 했었다. 다양한 Singleton 구현법에 대해 퍼포먼스를 측정하고 싶었지만, 저런 측정 방법이 영 미심쩍었다. 그래서 좀 더 정확하고

hyesun03.github.io

 

 

+ Recent posts