You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
그래서 이러한 단위 기능에 대한 성능 측정을 위한 도구, 흔히 벤치마크 도구라 불리는 툴들이 존재하고 JVM환경에서는 JMH를 많이 사용합니다.
사용 방법
크게 세 가지 방법이 존재합니다.
별도 애플리케이션에서 main 함수로 실행하는 방법 (추천 X)
jmh 플러그인과 디펜던시를 추가하여 실행하는 방법 (추천 O)
jmh 디펜던시를 추가하고 테스트 디렉토리 및 코드에서 실행하는 방법 (추천 O)
오늘은 예시는 3번 방법을 통해 간단히 성능 측정을 수행하는 방법을 안내합니다.
1번 방법의 경우 간단한 코드라면 복붙해서 테스트가 가능하겠지만, 일반적으로 여러 클래스를 별도 환경에서 복사해서 가져오는 건 매우 귀찮은 일입니다. 또한 벤치마크 코드를 소스코드 레포지터리에 함께 보관할 수 없기 때문에 추후 구성원들이 재측정 시 접근할 때 제약이 있습니다.
2, 3번의 경우 소스 코드 레포지터리에 함께 보관하고, 정의한 클래스를 바로 임포트해서 사용할 수 있기 때문에 편리하게 사용이 가능합니다.
2번 같은 경우 java-test-fixtures 플러그인을 사용할 때 처럼 src, test 외에 별도 디렉토리(jmh)를 만들고, 그곳에서 실행하는 것을 의미합니다.
Insight
이번 장에서 동시성 컬렉션을 다루게 되었는데요
관련하여 ArrayList에서 thread safe을 보장하는
CopyOnWriteArrayList
와SynchronizedList
두 가지 방법 중 정말CopyOnWriteArrayList
가 성능이 더 좋은지 확인해보는 간단한 실험을 진행해봤습니다.SynchronizedList
의 의미는Collections.synchronizedList(new ArrayList<>()
를 의미합니다.추가로 성능을 측정하기 위해 JVM 환경에서 많이 사용하는 JMH도 함께 안내해보고자 합니다.☺️
성능 측정과 JMH
성능 측정
보통 성능 측정을 진행한다고 하면 일반적으로 부하 테스트를 많이 생각하는데요.
하지만 위 방법의 경우 애플리케이션을 통째로 실행하고 통합적인 기능에 대한 성능을 측정하는 방법입니다.
단위 기능에 대한 성능 측정 관점에서 고려했을 때에는 너무 비싼 비용을 지불하여 테스트를 수행하게 됩니다.
간단하게 진행한다면
System.currentTimeMillis()
또는 Spring에서 제공하는StopWatch
를 이용하여 메서드의 수행시간을 측정해본 경험이 있을 것이라 생각됩니다.JMH (Java Microbenchmark Harness)
그래서 이러한 단위 기능에 대한 성능 측정을 위한 도구, 흔히 벤치마크 도구라 불리는 툴들이 존재하고 JVM환경에서는 JMH를 많이 사용합니다.
사용 방법
크게 세 가지 방법이 존재합니다.
1번 방법의 경우 간단한 코드라면 복붙해서 테스트가 가능하겠지만, 일반적으로 여러 클래스를 별도 환경에서 복사해서 가져오는 건 매우 귀찮은 일입니다. 또한 벤치마크 코드를 소스코드 레포지터리에 함께 보관할 수 없기 때문에 추후 구성원들이 재측정 시 접근할 때 제약이 있습니다.
2, 3번의 경우 소스 코드 레포지터리에 함께 보관하고, 정의한 클래스를 바로 임포트해서 사용할 수 있기 때문에 편리하게 사용이 가능합니다.
java-test-fixtures
플러그인을 사용할 때 처럼src
,test
외에 별도 디렉토리(jmh
)를 만들고, 그곳에서 실행하는 것을 의미합니다.디펜던시 추가
예시 코드
주요 어노테이션을 아래와 같습니다.
@State
: 벤치마크에 사용되어지는 Argument의 적용 범위 설정@BenchmarkMode
: 처리량(Throughput), 평균 시간(AverageTime) 등 측정할 메트릭 설정@Measurement
: 실행 당 측정 횟수 지정@OutputTimeUnit
: 출력 시간의 단위@Fork
: 실행 횟수@Benchmark
: 측정 대상CopyOnWriteArrayList
vsSynchronizedList
그렇다면 JMH를 이용해서 thread safe한 ArrayList의 성능을 비교해봅시다.
가설 및 방법
일반적으로는
CopyOnWriteArrayList
(낙관적 접근)이SynchronizedList
(비관적 접근)보다 성능이 좋을 것 같습니다.그렇다면 해당 리스트에 읽기/쓰기 작업의 비율을 바꿔가면서 모든 상황에서
CopyOnWriteArrayList
의 성능이 좋을지 확인을 해보면 됩니다.실험은 size가 1000인 각각의 리스트가 존재하고, 이 리스트를 아래의 읽기/쓰기 비율로 실행한 후 성능을 측정합니다.
실험 결과
메트릭 결과를 요약해보면 아래와 같습니다.
읽기 < 쓰기
CopyOnWriteArrayList
<SynchronizedList
읽기 = 쓰기
CopyOnWriteArrayList
>SynchronizedList
읽기 < 쓰기
CopyOnWriteArrayList
>SynchronizedList
결론
모든 상황에서
CopyOnWriteArrayList
가 성능이 우수할 줄 알았지만, 오히려 쓰기 비율이 많은 작업의 경우, 성능이 저하되는 현상이 발생했습니다.이에 대한 자세한 이유도 깊게 다루면 좋겠지만, 간단히 내용을 정리하면
CopyOnWriteArrayList
의 내부 구현에서 쓰기 작업이 발생하는 경우 리스트를 복제해서 데이터를 추가하는 형태로 구현이 되어집니다.따라서 데이터의 수가 많으면 많을수록
SynchronizedList
의 Lock 취득 대기시간보다 리스트 복제 시간이 길어지면서 오히려 성능이 저하되는 것을 유추해볼 수 있습니다.마지막으로 내용을 정리해보면 대부분의 경우에는 Synchronized를 사용하는 경우보다 동시성 컬렉션을 활용하는 경우가 더 좋은 성능을 기대할 수 있습니다.
하지만 개발이라는 환경에서는 Silver bullet은 없기에 주어진 요구사항 및 비즈니스 상황에 따라 적합한 자료 및 기술을 사용해야 할 것 같습니다.
물론
CopyOnWriteArrayList
가 본 실험 상황에서 약간 성능이 떨어졌을 뿐이고, 대부분의 경우에서는 더 좋은 성능이 나올 겁니다.실험의 요지는 모든 상황을 최적화해서 해결할 수 있는 완벽한 기술은 없고, 개선할 포인트들은 항상 존재하니까 열심히 일하자(?)
The text was updated successfully, but these errors were encountered: