벤치마킹(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

 

 

 

public enum TableStatus {
    Y("1", true),
    N("0", false);
    
    private String table1Value;
    private boolean table2Value;
    
    TableStatus(String table1Value, boolean table2Value) {
        this.table1Value = table1Value;
        this.table2Value = table2Value;
    }
}

 

Enum의 값 "Y", "N"에 따라 table1Value, table2Value의 값을 반환해주는 별도의 메소드를 선언하는 대신 Enum 클래스에서 위와 같이 묶어서 관리할 수 있다.

 

자바8 Function 참고

 

Java 8 - Function 예제

Java 8의 Function은 1개의 인자(Type T)를 받고 1개의 객체(Type R)를 리턴하는 함수형 인터페이스입니다. 다음 예제는 Function 구현 및 apply()가 어떻게 동작하는지 알려줍니다. 다음 예제는 andThen()이 어

codechacha.com

 

Enum 클래스로 상태 코드를 조회하고 각각의 코드에 따른 로직을 별도의 메소드를 통해 수행을 하도록 만들면 상태 코드의 조회와 코드에 따른 계산이 분리되어서 서로 관계가 있음을 표현할 수가 없다. 그래서 다음과 같이 Enum 클래스를 활용하여 계산 기능을 추가할 수 있다.

 

public enum CalculatorType {
    CALC_A(value -> value),
    CALC_B(value -> value * 10),
    CALC_C(value -> value * 3),
    CALC_ETC(value -> 0L);
    
    private Function<Long, Long>. expression;
    
    CalculatorType(Function<Long, Long> expression) {
        this.expression = expression;
    }
    
    public long calculate(long value) {
        return expresiion.apply(value);
    }
}

 

Function은 value1을 받아서 value2로 반환해주는 역할을 하며 apply()를 통해 인자값을 받으면 계산식을 거쳐서 결과를 반환 받을 수 있다.

 

핵심은 객체가 상태(값)와 행위(로직)을 갖고 있는 것으로 직접 활용을 안 해봐서 100% 이해는 못 했지만 이번 프로젝트에서 적절하게 적용해보고 직접 적용해본 내용을 정리해보면 좋을 것 같다.

 

 

 

Java Enum 활용기 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요? 우아한 형제들에서 결제/정산 시스템을 개발하고 있는 이동욱입니다. 이번 사내 블로그 포스팅 주제로 저는 Java Enum 활용 경험을 선택하였습니다. 이전에 개인 블로그에 E

techblog.woowahan.com

 

Legacy DB의 JPA Entity Mapping (Enum Converter 편) | 우아한형제들 기술블로그

{{item.name}} 안녕하세요. 저는 우아한형제들 비즈상품개발팀의 이은경입니다. Legacy DB의 JPA Entity Mapping (복합키 매핑 편)에 이어 저는 DB의 코드값과 Java Enum을 연결해주는 과정에서 유용하게 사용

techblog.woowahan.com

 

 

Enum 활용사례 3가지

안녕하세요? 이번 시간엔 enum 활용사례를 3가지정도 소개하려고 합니다. 모든 코드는 Github에 있기 때문에 함께 보시면 더 이해하기 쉬우실 것 같습니다. (공부한 내용을 정리하는 Github와 세미

jojoldu.tistory.com

 

 

자바 예외는 최상위 예외 계층인 Throwable, 그 하위로 Exception과 Error가 있는데 Error는 메모리 부족 같이 애플리케이션에서 복구 불가능한 시스템 예외인데 catch로 예외를 잡으면 하위 예외(Error)까지 함께 잡기 때문에 Exception 예외를 실질적인 최상위 예외로 생각하면 된다. 

 

Exception과 그 하위 예외는 RuntimeException을 제외하고 전부 컴파일러가 체크하는 체크 예외(잡아서 처리하거나 밖으로 던지지 않을 경우 컴파일 오류 발생)인데 RuntimeException은 컴파일러가 체크 하지 않는 언체크 예외(하위 예외 포함)로 런타임 예외라고 부른다.

 

예외는 잡아서 처리하거나 호출한 곳으로 던지게 되는데 잡거나(catch) 던질(throws) 경우 지정한 예외의 하위 예외들도 함께 처리가 된다. 예외를 처리하지 못할 경우 자바 main() 쓰레드는 예외 로그를 출력하면서 시스템이 종료되는데 웹 애플리케이션은 시스템이 종료되지 않는 대신 WAS가 해당 예외를 받아서 오류 페이지를 보여주는 식으로 처리를 한다.

 

체크 예외는 예외를 잡아서 처리하지 않는 경우 예외를 던지는 throws를 무조건 선언해야 하는데 예외를 누락하지 않도록 컴파일 시점에서 확인을 해준다는 장점이 있지만 실제로는 모든 예외를 전부 처리해야 되고 의존관계에 문제가 생기는 등의 단점이 있다.

언체크 예외는 말 그대로 컴파일러가 예외를 체크하지 않는 예외로 체크 예외와 달리 throws를 생략해도 되는데 중요한 예외의 경우에는 throws를 선언해서 코드를 호출하는 개발자에게 정보를 줄 수 있다.

 

기본적으로는 런타임 예외를 사용하고 체크 예외는 비즈니스 로직상 의도적으로 던지는, 반드시 처리해야 하는 문제에만 사용을 하는 것이 좋다. 예를 들어 JDBC를 사용하다가 JPA 같은 기술로 변경하는 경우 throws SQLException 코드를 전부 throws JPAException으로 바꿔야 하는 등 OCP, DI에 큰 문제가 생기는데 대부분의 예외는 서비스, 컨트롤러에서 복구 불가능한 예외라서 불필요한 의존관계 문제가 생기는 것이다.

 

복구 불가능한 예외의 경우 서블릿 필터나 스프링 인터셉터, 스프링 ControllerAdvice를 통해 일관성 있게 공통으로 처리해서 오류를 빠르게 인지하는 것이 중요한데 체크 예외를 런타임 예외로 변환하는 경우 예외를 일일히 throws 하지 않아도 되고 공통으로 처리를 하게 되면 변경시에도 영향 범위가 최소화 된다.

 

 

[참고] 인프런 김영한님 강의를 공부한 내용입니다.

+ Recent posts