반응형
Enum, int 형을 기준으로 조건문을 작성해야 하는 일이 있었습니다.
Java 버전이 업데이트되면서 switch 문이 조금씩 변경되는 것을 확인하고
if/else 문과 switch 문의 성능적으로 어떤 차이가 있는지 궁금했습니다.
또한 어떤 차이가 있고 어느 상황에서 switch문을 (if-else문) 사용하면 좋은지 정리해보고자 합니다.
Switch vs if/else
- if-else 문은 조건을 위에서부터 순차적으로 확인합니다.
- 각 조건을 순서대로 검사하기 때문에 조건이 많아지거나 복잡해질수록 성능 저하가 발생할 수 있습니다.
- switch 문은 조건이 int, char, enum, String 같은 특정 타입일 때 내부적으로 효율적인 분기 처리를 합니다.
- jump table, lookup table을 사용하여 분기를 처리해 if-else문보다 빠를 수 있습니다.
switch문은 jump table, lookup table을 통해 컴파일 시점에 최적화가 더 잘 이루어질 수 있습니다.
하지만, 조건의 개수가 별로 없거나 조건이 복잡한 경우에는 if/else이 더 단순하거나 효율적일 수 있습니다.
구현하려는 조건문의 형태와 개수를 생각해 보며 적절한 방법을 택하여 성능과 가독성을 높일 수 있었습니다.
Jump Table이란
switch 문의 조건이 연속된 int 값일 때 컴파일러가 사용하는 기법입니다.
switch 문이 많은 case를 포함하고 있을 때, 각 조건을 일일이 검사하는 대신 컴파일러는 값을 인덱스로 사용하는 배열과 유사한 자료 구조를 만들어 조건에 맞는 코드를 빠르게 실행하도록 합니다.
작동 방식
- 컴파일러는 switch 문의 case 값들을 배열의 인덱스처럼 사용하여, 각 값에 해당하는 분기점을 저장한 배열을 생성합니다.
- switch 문의 표현식이 평가되면, 그 결과 값이 배열의 인덱스가 되고 해당 인덱스에 미리 정의된 분기로 즉시 점프합니다.
- 이러한 과정을 통해 조건에 맞는 분기로 빠르게 이동할 수 있습니다.
성능
- Jump Table은 조건의 수에 상관없이 항상 O(1)에 분기할 수 있습니다. 많은 case가 있는 상황에서 빠르게 작동합니다.
- 해당 조건, 조건이 연속된 값이어야 가능하며, 값이 희소하게 분포되어 있는 경우에는 비효율적일 수 있습니다.
Lookup Table이란
Lookup Table은 switch 문의 case 값들이 비연속적이거나 문자열일 때 사용하는 기법입니다.
String 타입의 switch문은 내부적으로 String의 hashCode()를 사용하여 각 case 값을 비교합니다.
작동 방식
- 컴파일러는 switch문의 각 case 값에 대해 hashCode() 값을 계산하여 이를 키로 사용하는 해시 테이블을 생성합니다.
- switch 문의 표현식이 계산될 때, 해당 표현식의 결과 값을 계산하고, 해시 테이블을 통해 해당하는 분기로 빠르게 이동합니다.
- 해당 방식은 값들이 비연속적이거나 String과 같이 복잡한 타입일 때 유용합니다.
성능 비교하기
1. int 형 조건문인 경우
@Test
public void switchTest() {
// 테스트할 횟수
int testIterations = 100_000_000;
int value = 5; // 비교할 값
// 1. if-else 문 성능 테스트
long ifStartTime = System.nanoTime(); // 시작 시간 기록
for (int i = 0; i < testIterations; i++) {
if (value == 1) {
// 아무 동작도 하지 않음
} else if (value == 2) {
// 아무 동작도 하지 않음
} else if (value == 3) {
// 아무 동작도 하지 않음
} else if (value == 4) {
// 아무 동작도 하지 않음
} else if (value == 5) {
// 조건이 맞을 때
} ...
} else if (value == 9) {
// 아무 동작도 하지 않음
} else if (value == 10) {
// 아무 동작도 하지 않음
}
}
long ifEndTime = System.nanoTime(); // 끝 시간 기록
long ifDuration = ifEndTime - ifStartTime;
System.out.println("if-else 문 실행 시간: " + ifDuration + " 나노초");
// 2. switch 문 성능 테스트
long switchStartTime = System.nanoTime(); // 시작 시간 기록
for (int i = 0; i < testIterations; i++) {
switch (value) {
case 1:
// 아무 동작도 하지 않음
break;
case 2:
// 아무 동작도 하지 않음
break;
case 3:
// 아무 동작도 하지 않음
break;
case 4:
// 아무 동작도 하지 않음
break;
case 5:
// 조건이 맞을 때
break;
...
case 9:
// 아무 동작도 하지 않음
break;
case 10:
// 아무 동작도 하지 않음
break;
}
}
long switchEndTime = System.nanoTime(); // 끝 시간 기록
long switchDuration = switchEndTime - switchStartTime;
System.out.println("switch 문 실행 시간: " + switchDuration + " 나노초");
// 성능 비교
if (ifDuration < switchDuration) {
System.out.println("if-else 문이 더 빠릅니다.");
} else if (switchDuration < ifDuration) {
System.out.println("switch 문이 더 빠릅니다.");
} else {
System.out.println("if-else 문과 switch 문의 성능이 동일합니다.");
}
}
switch 문이 if-else 문보다 약 1.2배 빠르다는 것을 알 수 있습니다.
2. Enum(String) 조건문인 경우
@Test
public void switchEnumTest() {
// enum 선언
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
// 테스트할 횟수
int testIterations = 100_000_000;
Day today = Day.FRIDAY; // 비교할 enum 값
// 1. if-else 문 성능 테스트
long ifStartTime = System.nanoTime(); // 시작 시간 기록
for (int i = 0; i < testIterations; i++) {
if (today == Day.MONDAY) {
// 아무 동작도 하지 않음
} else if (today == Day.TUESDAY) {
// 아무 동작도 하지 않음
} else if (today == Day.WEDNESDAY) {
// 아무 동작도 하지 않음
} else if (today == Day.THURSDAY) {
// 아무 동작도 하지 않음
} else if (today == Day.FRIDAY) {
// 조건이 맞을 때
} else if (today == Day.SATURDAY) {
// 아무 동작도 하지 않음
} else if (today == Day.SUNDAY) {
// 아무 동작도 하지 않음
}
}
long ifEndTime = System.nanoTime(); // 끝 시간 기록
long ifDuration = ifEndTime - ifStartTime;
System.out.println("if-else 문(enum) 실행 시간: " + ifDuration + " 나노초");
// 2. switch 문 성능 테스트
long switchStartTime = System.nanoTime(); // 시작 시간 기록
for (int i = 0; i < testIterations; i++) {
switch (today) {
case MONDAY:
// 아무 동작도 하지 않음
break;
case TUESDAY:
// 아무 동작도 하지 않음
break;
case WEDNESDAY:
// 아무 동작도 하지 않음
break;
case THURSDAY:
// 아무 동작도 하지 않음
break;
case FRIDAY:
// 조건이 맞을 때
break;
case SATURDAY:
// 아무 동작도 하지 않음
break;
case SUNDAY:
// 아무 동작도 하지 않음
break;
}
}
long switchEndTime = System.nanoTime(); // 끝 시간 기록
long switchDuration = switchEndTime - switchStartTime;
System.out.println("switch 문(enum) 실행 시간: " + switchDuration + " 나노초");
// 성능 비교
if (ifDuration < switchDuration) {
System.out.println("if-else 문(enum)이 더 빠릅니다.");
} else if (switchDuration < ifDuration) {
System.out.println("switch 문(enum)이 더 빠릅니다.");
} else {
System.out.println("if-else 문(enum)과 switch 문의 성능이 동일합니다.");
}
}
switch 문이 if-else 문보다 약 1.8배 빠르다는 것을 알 수 있습니다.
반응형
'JAVA' 카테고리의 다른 글
[Java] 자바 추상화 설계 이해하기 - 추상 클래스와 인터페이스 활용 (0) | 2024.09.21 |
---|---|
[Java] Java of, from, parse 정적 팩토리 메서드 이해하기 - Method Naming Convention (0) | 2024.09.19 |
[Java] 자바 인코딩, 디코딩, base64 인코딩, 디코딩 처리하기 (1) | 2024.09.14 |
[JAVA] static, final, staic final 개념 이해하기 - 전역 변수, 상수 (0) | 2024.09.14 |
[JAVA] Java 일급 컬렉션 사용법 및 이해하기 (1) | 2024.09.08 |