제리의 배움 기록
[Java] if, switch 누가 더 빠를까? 본문
조건에 따라 실행 흐름을 분기해야 하는 경우 우리는 조건문을 사용합니다.
Java에는 if-else
/ switch
두가지 조건문이 있습니다.
이 중 어떤 방식이 더 효율적인지, 무엇을 쓰는게 좋을지 비교해보고자 합니다.
우선, switch를 사용할 수 없는 것들은 선택의 여지 없이 if-else를 사용하면 됩니다.
다음으로 if-else, switch 두가지 방식 모두 사용할 수 있는 경우가 있습니다.
동일한 로직을 두 방법으로 구현하여 변환된 바이트 코드를 비교해보며 차이를 알아보겠습니다.
if
자바코드
public class ConditionTest1Main {
void doIfStatement() {
int number = 5;
if (number == 1) {
method1();
} else if (number == 2) {
method2();
} else {
method3();
}
}
void method1(){
System.out.println("method1");
}
void method2(){
System.out.println("method1");
}
void method3(){
System.out.println("method1");
}
}
바이트코드
// class version 52.0 (52)
// access flags 0x21
public class ConditionTest1Main {
// compiled from: ConditionTest1Main.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 1 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this LConditionTest1Main; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x0
doIfStatement()V
L0
LINENUMBER 4 L0
ICONST_5
ISTORE 1
L1
LINENUMBER 6 L1
ILOAD 1
ICONST_1
IF_ICMPNE L2
L3
LINENUMBER 7 L3
ALOAD 0
INVOKEVIRTUAL ConditionTest1Main.method1 ()V
GOTO L4
L2
LINENUMBER 8 L2
FRAME APPEND [I]
ILOAD 1
ICONST_2
IF_ICMPNE L5
L6
LINENUMBER 9 L6
ALOAD 0
INVOKEVIRTUAL ConditionTest1Main.method2 ()V
GOTO L4
L5
LINENUMBER 11 L5
FRAME SAME
ALOAD 0
INVOKEVIRTUAL ConditionTest1Main.method3 ()V
L4
LINENUMBER 13 L4
FRAME SAME
RETURN
L7
LOCALVARIABLE this LConditionTest1Main; L0 L7 0
LOCALVARIABLE number I L1 L7 1
MAXSTACK = 2
MAXLOCALS = 2
...
}
L1부터 L4까지가 if문이 변환된 바이트코드입니다.
1)L1는 비교로직을 수행하고 조건문을 통과하면 L3가 실행되고, L4로 넘어가 if문이 종료되지만,
통과하지 못하면 IF_ICMPNE L2
로 L2로 넘어가게 됩니다.
2)L2로 넘어가 비교로직을 수행하고 조건문을 통과하면 L6 수행후 L4로 넘어가 if문이 종료되지만,
통과하지 못하면 IF_ICMPNE L5
로 L5로 넘어가게 됩니다.
3)L5는 else 조건이기 때문에 비교로직 없이 내부 로직이 수행되고 L4로 넘어가 if문을 종료하게 됩니다.
이 과정을 보면 if-else 구문은, true가 되는 조건을 만나기까지 if절에 작성된 차례대로 비교연산을 계속해서 수행하여야 합니다.
if절이 10개 정도 된다면, 마지막 조건에 걸리는 값이더라도 항상 앞에 9번의 조건을 수행해야하는 비효율이 있습니다.
[특징 요약]
- if-else 구문의 조건들 중 만족하는 조건이 나타낼 때 까지 조건을 체크합니다.
- 따져야 하는 조건이 많을 수록 연산량이 늘어납니다.
- 확률이 높은 조건부터 작성하면, 조건 체크 연산이 줄어드므로 효율적입니다.
=> 조건들의 확률이 비슷하지 않고 특정 조건의 확률이 높은 경우는 switch 보다 빠를 수 있습니다.
Switch
자바코드
(if를 사용한 코드에서 doIfStatement 메서드 내부 구현만 swtich로 변경)
void doIfStatement() {
int number = 5;
switch (number){
case 1:
method1();
break;
case 2:
method2();
break;
default:
method3();
}
}
바이트코드
// class version 52.0 (52)
// access flags 0x21
public class ConditionTest2Main {
// compiled from: ConditionTest2Main.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 1 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this LConditionTest2Main; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x0
doIfStatement()V
L0
LINENUMBER 4 L0
ICONST_5
ISTORE 1
L1
LINENUMBER 6 L1
ILOAD 1
LOOKUPSWITCH
1: L2
2: L3
default: L4
L2
LINENUMBER 8 L2
FRAME APPEND [I]
ALOAD 0
INVOKEVIRTUAL ConditionTest2Main.method1 ()V
L5
LINENUMBER 9 L5
GOTO L6
L3
LINENUMBER 11 L3
FRAME SAME
ALOAD 0
INVOKEVIRTUAL ConditionTest2Main.method2 ()V
L7
LINENUMBER 12 L7
GOTO L6
L4
LINENUMBER 14 L4
FRAME SAME
ALOAD 0
INVOKEVIRTUAL ConditionTest2Main.method3 ()V
L6
LINENUMBER 16 L6
FRAME SAME
RETURN
...
}
L1부터 L4까지 switch문이 변환된 바이트코드 입니다.
L1을 보시면 if-else와의 가장 큰 차이인 LOOKUPSWITCH
구문을 보실 수 있습니다.LOOKUPSWITCH
방식은 if-else 처럼 비교연산을 하지 않고 표현식 값을 이용해 탐색함으로써 내부적으로 아래와 같이 값에 해당하는 jump table을 생성됩니다.
LOOKUPSWITCH
1: L2
2: L3
default: L4
만족하는 조건값을 찾을때까지 비교연산 없이 바로 해당 실행구문으로 점프할 수 있다는 점에서 효율적이지만,
jump table 생성 비용의 오버헤드가 있습니다.
[요약]
- 조건 값을 이용하여 빠른시간에 탐색할 수 있는 jump table을 생성합니다.
- 조건이 많을 때와 여러기능을 유사한 빈도로 사용할 때는 if-else 보다 성능이 빠를 수 있습니다.
'자바' 카테고리의 다른 글
[Java] 프로세스와 스레드 - <1> JVM 메모리 관점에서 분석 (0) | 2021.12.15 |
---|---|
[Java] 프로세스와 스레드 - <2> LifeCycle 비교 (0) | 2021.12.14 |
[Java] Compiling Switches : tableswitch 와 lookupswitch (0) | 2021.12.07 |
[Java] JVM 성능 모니터링을 제공하는 JDK 기본 툴 (0) | 2021.12.07 |
[Java] String + 연산 최적화 (0) | 2021.12.02 |