Java에서 if / else와 switch 문의 상대적인 성능 차이는 무엇입니까?
웹 응용 프로그램의 성능에 대해 걱정할 때 성능과 관련하여 "if / else"또는 switch 문 중 어떤 것이 더 좋은지 궁금합니다.
그것은 미세한 최적화와 조기 최적화입니다. 오히려 문제의 코드의 가독성과 유지 보수성에 대해 걱정하십시오. if/else
서로 붙어있는 블록 이 두 개 이상 이거나 크기가 예측할 수없는 경우, switch
성명서를 고려할 수 있습니다 .
또는 다형성 을 가져올 수도 있습니다 . 먼저 몇 가지 인터페이스를 만듭니다.
public interface Action {
void execute(String input);
}
그리고 일부의 모든 구현을 파악하십시오 Map
. 이 작업은 정적으로 또는 동적으로 수행 할 수 있습니다.
Map<String, Action> actions = new HashMap<String, Action>();
마지막으로 교체 if/else
또는 switch
이 같은으로 (옆 nullpointers 같은 사소한 검사를 떠나) :
actions.get(name).execute(input);
그것은 수 보다 microslower 일 if/else
이나 switch
,하지만 코드는 적어도 훨씬 더 유지 보수입니다.
웹 응용 프로그램에 대해 이야기 HttpServletRequest#getPathInfo()
하면서 액션 키로 사용할 수 있습니다 (결국 패스 정보의 마지막 부분을 액션이 발견 될 때까지 루프로 분리하는 코드를 더 작성하십시오). 비슷한 답변을 찾을 수 있습니다.
일반적으로 Java EE 웹 애플리케이션 성능에 대해 걱정하는 경우이 기사도 유용 할 수 있습니다. 원시 Java 코드를 최적화하는 것보다 훨씬 더 많은 성능 향상 을 제공하는 다른 영역이 있습니다.
조기 최적화는 피해야한다는 의견에 전적으로 동의합니다.
그러나 Java VM에는 switch ()에 사용할 수있는 특수 바이트 코드가 있다는 것이 사실입니다.
WM 사양 참조 ( lookupswitch 및 tableswitch )
따라서 코드가 성능 CPU 그래프의 일부인 경우 성능이 약간 향상 될 수 있습니다.
if / else 또는 스위치가 성능 문제의 원인이 될 가능성은 거의 없습니다. 성능 문제가있는 경우 먼저 성능 프로파일 링 분석을 수행하여 느린 지점의 위치를 결정해야합니다. 조기 최적화는 모든 악의 근원입니다!
그럼에도 불구하고 Java 컴파일러 최적화를 통해 스위치와 if / else의 상대적인 성능에 대해 이야기 할 수 있습니다. 우선 Java에서 switch 문은 매우 제한된 도메인 (정수)에서 작동합니다. 일반적으로 다음과 같이 switch 문을 볼 수 있습니다.
switch (<condition>) {
case c_0: ...
case c_1: ...
...
case c_n: ...
default: ...
}
where c_0
,, c_1
..., 및 c_N
switch 문의 대상인 <condition>
정수이며 정수 표현식으로 해석되어야합니다.
인 (최대 (c - 세트는 "밀도"는 경우 나 ) + - 1 분 (c I )) / N> α, 0 <K <α <1 여기서
k
경험적 값,보다 큰 점프 테이블을 생성 할 수있어 매우 효율적입니다.이 세트가 밀도가 높지 않지만 n> = β 인 경우 이진 검색 트리는 여전히 효율적인 O (2 * log (n))에서 대상을 찾을 수 있습니다.
다른 모든 경우에 대해 switch 문은 동일한 일련의 if / else 문만큼 효율적입니다. α 및 β의 정확한 값은 여러 가지 요인에 따라 달라지며 컴파일러의 코드 최적화 모듈에 의해 결정됩니다.
마지막으로의 도메인이 <condition>
정수가 아닌 경우 switch 문은 완전히 쓸모가 없습니다.
스위치를 사용하십시오!
if-else-blocks를 유지하는 것을 싫어합니다! 테스트를 해보십시오 :
public class SpeedTestSwitch
{
private static void do1(int loop)
{
int temp = 0;
for (; loop > 0; --loop)
{
int r = (int) (Math.random() * 10);
switch (r)
{
case 0:
temp = 9;
break;
case 1:
temp = 8;
break;
case 2:
temp = 7;
break;
case 3:
temp = 6;
break;
case 4:
temp = 5;
break;
case 5:
temp = 4;
break;
case 6:
temp = 3;
break;
case 7:
temp = 2;
break;
case 8:
temp = 1;
break;
case 9:
temp = 0;
break;
}
}
System.out.println("ignore: " + temp);
}
private static void do2(int loop)
{
int temp = 0;
for (; loop > 0; --loop)
{
int r = (int) (Math.random() * 10);
if (r == 0)
temp = 9;
else
if (r == 1)
temp = 8;
else
if (r == 2)
temp = 7;
else
if (r == 3)
temp = 6;
else
if (r == 4)
temp = 5;
else
if (r == 5)
temp = 4;
else
if (r == 6)
temp = 3;
else
if (r == 7)
temp = 2;
else
if (r == 8)
temp = 1;
else
if (r == 9)
temp = 0;
}
System.out.println("ignore: " + temp);
}
public static void main(String[] args)
{
long time;
int loop = 1 * 100 * 1000 * 1000;
System.out.println("warming up...");
do1(loop / 100);
do2(loop / 100);
System.out.println("start");
// run 1
System.out.println("switch:");
time = System.currentTimeMillis();
do1(loop);
System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
// run 2
System.out.println("if/else:");
time = System.currentTimeMillis();
do2(loop);
System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
}
}
Java 바이트 코드에는 2 종류의 Switch 문이 있다는 것을 기억합니다. (저는 'Java Performance Tuning'에 있다고 생각합니다. 하나는 switch 문의 정수 값을 사용하여 실행될 코드의 오프셋을 아는 매우 빠른 구현입니다. 모든 정수가 연속적이고 잘 정의 된 범위에 있어야합니다. Enum의 모든 값을 사용하면 해당 범주에 속한다고 생각합니다.
나는 다른 많은 포스터에 동의하지만 ... 매우 매우 뜨거운 코드가 아니라면 이것에 대해 걱정하는 것이 너무 빠를 수 있습니다.
그의 2009 자바 하나 개의 이야기에서 절벽 클릭에 따르면 현대 하드웨어에서의 충돌 코스 :
오늘날 성능은 메모리 액세스 패턴에 의해 좌우됩니다. 캐시 미스가 지배적이다 – 메모리는 새로운 디스크이다. [슬라이드 65]
여기에서 그의 전체 슬라이드를 얻을 수 있습니다 .
Cliff는 CPU에서 레지스터 이름 바꾸기, 분기 예측 및 추론 적 실행을 수행하더라도 2 개의 캐시 미스로 인해 차단하기 전에 4 클럭주기에서 7 개 작업 만 시작할 수 있음을 보여주는 예제 (슬라이드 30에서 마무리)를 제공합니다. 300 클럭 주기로 돌아갑니다.
그는 프로그램 속도를 높이기 위해 이런 종류의 사소한 문제를 보지 말고 "SOAP → XML → DOM → SQL → ...과 같은 불필요한 데이터 형식 변환을 수행하는지 여부와 같은 큰 문제에 대해서는 ""캐시를 통해 모든 데이터를 전달합니다 ".
내 테스트에서 더 나은 성능은 Windows7의 ENUM> MAP> SWITCH> IF / ELSE IF 입니다.
import java.util.HashMap;
import java.util.Map;
public class StringsInSwitch {
public static void main(String[] args) {
String doSomething = null;
//METHOD_1 : SWITCH
long start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
switch (input) {
case "Hello World0":
doSomething = "Hello World0";
break;
case "Hello World1":
doSomething = "Hello World0";
break;
case "Hello World2":
doSomething = "Hello World0";
break;
case "Hello World3":
doSomething = "Hello World0";
break;
case "Hello World4":
doSomething = "Hello World0";
break;
case "Hello World5":
doSomething = "Hello World0";
break;
case "Hello World6":
doSomething = "Hello World0";
break;
case "Hello World7":
doSomething = "Hello World0";
break;
case "Hello World8":
doSomething = "Hello World0";
break;
case "Hello World9":
doSomething = "Hello World0";
break;
case "Hello World10":
doSomething = "Hello World0";
break;
case "Hello World11":
doSomething = "Hello World0";
break;
case "Hello World12":
doSomething = "Hello World0";
break;
case "Hello World13":
doSomething = "Hello World0";
break;
case "Hello World14":
doSomething = "Hello World0";
break;
case "Hello World15":
doSomething = "Hello World0";
break;
}
}
System.out.println("Time taken for String in Switch :"+ (System.currentTimeMillis() - start));
//METHOD_2 : IF/ELSE IF
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
if(input.equals("Hello World0")){
doSomething = "Hello World0";
} else if(input.equals("Hello World1")){
doSomething = "Hello World0";
} else if(input.equals("Hello World2")){
doSomething = "Hello World0";
} else if(input.equals("Hello World3")){
doSomething = "Hello World0";
} else if(input.equals("Hello World4")){
doSomething = "Hello World0";
} else if(input.equals("Hello World5")){
doSomething = "Hello World0";
} else if(input.equals("Hello World6")){
doSomething = "Hello World0";
} else if(input.equals("Hello World7")){
doSomething = "Hello World0";
} else if(input.equals("Hello World8")){
doSomething = "Hello World0";
} else if(input.equals("Hello World9")){
doSomething = "Hello World0";
} else if(input.equals("Hello World10")){
doSomething = "Hello World0";
} else if(input.equals("Hello World11")){
doSomething = "Hello World0";
} else if(input.equals("Hello World12")){
doSomething = "Hello World0";
} else if(input.equals("Hello World13")){
doSomething = "Hello World0";
} else if(input.equals("Hello World14")){
doSomething = "Hello World0";
} else if(input.equals("Hello World15")){
doSomething = "Hello World0";
}
}
System.out.println("Time taken for String in if/else if :"+ (System.currentTimeMillis() - start));
//METHOD_3 : MAP
//Create and build Map
Map<String, ExecutableClass> map = new HashMap<String, ExecutableClass>();
for (int i = 0; i <= 15; i++) {
String input = "Hello World" + (i & 0xF);
map.put(input, new ExecutableClass(){
public void execute(String doSomething){
doSomething = "Hello World0";
}
});
}
//Start test map
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
map.get(input).execute(doSomething);
}
System.out.println("Time taken for String in Map :"+ (System.currentTimeMillis() - start));
//METHOD_4 : ENUM (This doesn't use muliple string with space.)
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "HW" + (i & 0xF);
HelloWorld.valueOf(input).execute(doSomething);
}
System.out.println("Time taken for String in ENUM :"+ (System.currentTimeMillis() - start));
}
}
interface ExecutableClass
{
public void execute(String doSomething);
}
// Enum version
enum HelloWorld {
HW0("Hello World0"), HW1("Hello World1"), HW2("Hello World2"), HW3(
"Hello World3"), HW4("Hello World4"), HW5("Hello World5"), HW6(
"Hello World6"), HW7("Hello World7"), HW8("Hello World8"), HW9(
"Hello World9"), HW10("Hello World10"), HW11("Hello World11"), HW12(
"Hello World12"), HW13("Hello World13"), HW14("Hello World4"), HW15(
"Hello World15");
private String name = null;
private HelloWorld(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void execute(String doSomething){
doSomething = "Hello World0";
}
public static HelloWorld fromString(String input) {
for (HelloWorld hw : HelloWorld.values()) {
if (input.equals(hw.getName())) {
return hw;
}
}
return null;
}
}
//Enum version for betterment on coding format compare to interface ExecutableClass
enum HelloWorld1 {
HW0("Hello World0") {
public void execute(String doSomething){
doSomething = "Hello World0";
}
},
HW1("Hello World1"){
public void execute(String doSomething){
doSomething = "Hello World0";
}
};
private String name = null;
private HelloWorld1(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void execute(String doSomething){
// super call, nothing here
}
}
/*
* http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string
* https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10
* http://forums.xkcd.com/viewtopic.php?f=11&t=33524
*/
For most switch
and most if-then-else
blocks, I can't imagine that there are any appreciable or significant performance related concerns.
But here's the thing: if you're using a switch
block, its very use suggests that you're switching on a value taken from a set of constants known at compile time. In this case, you really shouldn't be using switch
statements at all if you can use an enum
with constant-specific methods.
Compared to a switch
statement, an enum provides better type safety and code that is easier to maintain. Enums can be designed so that if a constant is added to the set of constants, your code won't compile without providing a constant-specific method for the new value. On the other hand, forgetting to add a new case
to a switch
block can sometimes only be caught at run time if you're lucky enough to have set your block up to throw an exception.
Performance between switch
and an enum
constant-specific method should not be significantly different, but the latter is more readable, safer, and easier to maintain.
'development' 카테고리의 다른 글
Swift의 Documents 디렉토리에 파일이 있는지 확인하는 방법은 무엇입니까? (0) | 2020.07.17 |
---|---|
Github 권한 거부 : ssh add 에이전트에 ID가 없습니다 (0) | 2020.07.17 |
힘내 : 상태, diff 등의 '수정 된 콘텐츠'/ 더러운 하위 모듈 항목의 목록을 표시하지 않을 수 있습니까? (0) | 2020.07.17 |
jQuery, 전체 요소의 html 가져 오기 (0) | 2020.07.17 |
JavaScript에서 문자열에 하위 문자열 배열의 텍스트가 포함되어 있는지 확인하는 방법은 무엇입니까? (0) | 2020.07.17 |