티스토리 뷰
JMH 툴을 통해 Collection 관련 객체에 대해 성능 테스트를 진행 해보자.
JMH 툴 설치가 아직 안되신 분들은 JMH 설치 설치 및 설정 방법 글을 참고 하기 바란다.
[엔지니어링/성능과 튜닝] - [성능과 튜닝] JMH 설치 및 설정 방법
Set 클래스 중 무엇이 가장 빠를까?
Set 관련 클래스들의 성능을 비교해 보기 위해 아래와 같이 JMH 테스트 코드를 만들었다.
데이터를 담을 때 얼마나 시간 차이가 발생하는지 확인해 보자.
@State(Scope.Thread) @BenchmarkMode({Mode.AverageTime}) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class SetAdd { int LOOP_CONUT = 1000; Set<String> set; String data = "abcdefghijklmnopqrstuvwxyz"; @Benchmark public void addHashSet() { set = new HashSet<String>(); for (int loop = 0; loop < LOOP_CONUT; loop++) { set.add(data + loop); } } @Benchmark public void addTreeSet() { set = new TreeSet<String>(); for (int loop = 0; loop < LOOP_CONUT; loop++) { set.add(data + loop); } } @Benchmark public void addLinkedHashSet() { set = new LinkedHashSet<String>(); for (int loop = 0; loop < LOOP_CONUT; loop++) { set.add(data + loop); } } @Benchmark public void addHashSetWithInitialSize() { set = new HashSet<String>(LOOP_CONUT); for (int loop = 0; loop < LOOP_CONUT; loop++) { set.add(data + loop); } } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(SetAdd.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }
HashSet과 LinkedHashSet의 성능이 비슷하고, TreeSet은 성능 차이가 발생한다. 또한 데이터 크기를 미리 알고 있을 때 미세하지만 성능상 유리하다.
이번에는 Set 클래스들이 데이터를 읽을 떄 얼마나 차이가 발생하는지 확인해 보자.
@State(Scope.Thread) @BenchmarkMode({Mode.AverageTime}) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class SetIterate { int LOOP_COUNT = 1000; Set<String> hashSet; Set<String> treeSet; Set<String> linkedHashSet; String data = "abcdefghijklmnopqrstuvwxyz"; String[] keys; String result = null; @Setup(Level.Trial) public void setUp() { hashSet = new HashSet<String>(); treeSet = new TreeSet<String>(); linkedHashSet = new LinkedHashSet<>(); for (int loop = 0; loop < LOOP_COUNT; loop++) { String tempData = data + loop; hashSet.add(tempData); treeSet.add(tempData); linkedHashSet.add(tempData); } } @Benchmark public void iteaterHashSet() { Iterator<String> iter = hashSet.iterator(); while(iter.hasNext()) { result = iter.next(); } } @Benchmark public void iterateTreeSet() { Iterator<String> iter = treeSet.iterator(); while (iter.hasNext()) { result = iter.next(); } } @Benchmark public void iterateLinkedHashSet() { Iterator<String> iter = linkedHashSet.iterator(); while (iter.hasNext()) { result = iter.next(); } } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(SetIterate.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }
LinkedHashSet이 가장 빠르고, HashSet, TreeSet 순으롣 데이터를 가져오는 속도가 느려진다.
일반적으로 Set은 여러 데이터를 넣어 두고 해당 데이터가 존재하는지를 확인하는 용도로 많이 사용된다. 따라서 데이터를 램덤하게 가져와서 재 테스트를 해보자.
public class RandomKeyUtil { public static String[] generateRandomSetKeysSwap(Set<String> set) { int size = set.size(); String[] result = new String[size]; Random random = new Random(); int maxNumber = size; Iterator<String> iterator = set.iterator(); int resultPos = 0; while (iterator.hasNext()) { result[resultPos++] = iterator.next(); } for (int loop = 0; loop < size; loop++) { int randomNumber1 = random.nextInt(maxNumber); int randomNumber2 = random.nextInt(maxNumber); String temp = result[randomNumber2]; result[randomNumber2] = result[randomNumber1]; result[randomNumber1] = temp; } return result; } public static int[] generateRandomNumberKeysSwap(int loop_count) { int[] result = new int[loop_count]; Random random = new Random(); for (int i = 0; i < loop_count; i++) { int randomNumber = random.nextInt(loop_count); result[i] = randomNumber; } return result; } }
@State(Scope.Thread) @BenchmarkMode({Mode.AverageTime}) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class SetContains { int LOOP_COUNT = 1000; Set<String> hashSet; Set<String> treeSet; Set<String> linkedHashSet; String data = "abcdefghijklmnopqrstuvwxyz"; String[] keys; String result = null; @Setup(Level.Trial) public void setUp() { hashSet = new HashSet<String>(); treeSet = new TreeSet<String>(); linkedHashSet = new LinkedHashSet<>(); for (int loop = 0; loop < LOOP_COUNT; loop++) { String tempData = data + loop; hashSet.add(tempData); treeSet.add(tempData); linkedHashSet.add(tempData); } if (keys == null || keys.length != LOOP_COUNT) { keys = RandomKeyUtil.generateRandomSetKeysSwap(hashSet); } } @Benchmark public void containsHashSet() { for (String key : keys) { hashSet.contains(key); } } @Benchmark public void containsTreeSet() { for (String key : keys) { treeSet.contains(key); } } @Benchmark public void containsLinkedHashSet() { for (String key : keys) { linkedHashSet.contains(key); } } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(SetContains.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }
HashSet과 LinkedHashSet의 속도는 빠르지만, TreeSet의 속도는 느리다는 것을 알 수 있다. TreeSet을 만든 이유가 무엇일까?
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
TreeSet은 데이터를 저장하면서 정렬한다. TreeSet 인터페이스 중에 NavigableSet이 있다. 이 인터페이스는 특정 값보다 큰 값이나, 작은 값, 가장 큰 값, 가장 작은 값 등을 추출하는 메서드를 선언해 놓았으며 JDK 1.6부터 추가된 것이다.
즉, 데이터를 순서에 따라 탐색하는 작업이 필요 할때는 TreeSet을 사용하는 것이 좋다. 그럴 필요가 없을 때는 HashSet이나 LinkedHashSet을 사용하는 것을 권장한다.
'프로그래밍 > 자바 성능 튜닝 이야기' 카테고리의 다른 글
[자바 성능 튜닝 이야기] Map 어디에 담아야 하는지... (0) | 2019.04.04 |
---|---|
[자바 성능 튜닝 이야기] List 어디에 담아야 하는지... (0) | 2019.04.03 |
[자바 성능 튜닝 이야기] 왜 자꾸 String을 쓰지 말라는거야 (0) | 2019.02.23 |
[자바 성능 튜닝 이야기] 내가 만든 프로그램의 속도를 알고 싶다. (0) | 2019.02.23 |
[자바 성능 튜닝 이야기] 디자인 패턴, 꼭 써야 한다. (0) | 2019.02.21 |
- Linux 명령어
- React
- spring
- 프로그래머스
- 프로그래머
- 오라클
- 정렬 알고리즘
- Tomcat
- 이직
- 자바스크립트
- 제주도 여행
- 자바
- Maven
- Java
- 제주도 3박4일 일정
- 소프트웨어공학
- 회고
- 리눅스 명령어
- 리액트
- effective java
- 성능분석
- SQL
- Collection
- 경력관리
- Eclipse
- 오라클 내장 함수
- javascript
- 개발환경
- sort algorithm
- 리액트 16
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |