switch vs if 어떤 때 어느게 효율적인가요?

sypark33의 이미지

저는 조건문이 4개 이하이면 그냥 if, else if 로 하고, 그 이상이면 switch case 문을 사용하고 있습니다.

그냥 짧은 것은 switch문 쓰면 너무 소스가 길어져서..

그런데, 어떤게 어느때 사용해야 제대로 사용하는것인지 알고 싶더군요.
얼핏 들어본 기억으로는 ,
1. if문은 매번 조건문을 판단해야 하기에 switch문보다 느리다.
2. switch문은 case문 선별에 있에 stack에 값을 놓고 비교하기에, 느릴 수 있다..

뭐 이렇게 기억되는데 맞나요? embeded쪽이라 효율을 신경써야하는데, 이제껏 별 신경안쓰고 코딩하다가 문득 궁금합니다.

notexist의 이미지

잘 아는 것은 아니지만...switch를 사용하는 것이 더 좋은 것으로 알고 있습니다.
모든 switch는 if로 표현가능하나 모든 if는 switch로 표현할 수 있는 건 아니죠.
컴파일러가 최적화할때 switch쪽이 더 유리한 것으로 알고 있습니다.
예를 들면 각 case의 위치를 미리 배열로 만들어 놓고 바로 해당 위치로 점프를 한다던지 하는 식도 있겠고요.

sypark33 wrote:
저는 조건문이 4개 이하이면 그냥 if, else if 로 하고, 그 이상이면 switch case 문을 사용하고 있습니다.

그냥 짧은 것은 switch문 쓰면 너무 소스가 길어져서..

그런데, 어떤게 어느때 사용해야 제대로 사용하는것인지 알고 싶더군요.
얼핏 들어본 기억으로는 ,
1. if문은 매번 조건문을 판단해야 하기에 switch문보다 느리다.
2. switch문은 case문 선별에 있에 stack에 값을 놓고 비교하기에, 느릴 수 있다..

뭐 이렇게 기억되는데 맞나요? embeded쪽이라 효율을 신경써야하는데, 이제껏 별 신경안쓰고 코딩하다가 문득 궁금합니다.

There is more than one way to do it...

hyperhidrosis의 이미지

저도 평소에 궁금해 하던 부분이어서


int main()
{
	int x=0;
	int y;

	if(x==0)  y = 0;
	else if(x==1) y = 1;
	else if(x==2) y = 2;
	else if(x==5) y = 5;
	else if(x==6) y = 6;


	switch(x)
	{
	case 0 : y = 0; break;
	case 1 : y = 1; break;
	case 4 : y = 4; break;
	case 5 : y = 5; break;
	case 6 : y = 6; break;
		
	}

	return 0;
}

이런 소스를 짜놓고 vc 로 역어셈블 해봤습니다.

7:        if(x==0)  y = 0;
0040D4BF   cmp         dword ptr [ebp-4],0
0040D4C3   jne         main+2Eh (0040d4ce)
0040D4C5   mov         dword ptr [ebp-8],0
8:        else if(x==1) y = 1;
0040D4CC   jmp         main+68h (0040d508)
0040D4CE   cmp         dword ptr [ebp-4],1
0040D4D2   jne         main+3Dh (0040d4dd)
0040D4D4   mov         dword ptr [ebp-8],1
9:        else if(x==2) y = 2;
0040D4DB   jmp         main+68h (0040d508)
0040D4DD   cmp         dword ptr [ebp-4],2
0040D4E1   jne         main+4Ch (0040d4ec)
0040D4E3   mov         dword ptr [ebp-8],2
10:       else if(x==5) y = 5;
0040D4EA   jmp         main+68h (0040d508)
0040D4EC   cmp         dword ptr [ebp-4],5
0040D4F0   jne         main+5Bh (0040d4fb)
0040D4F2   mov         dword ptr [ebp-8],5
11:       else if(x==6) y = 6;
0040D4F9   jmp         main+68h (0040d508)
0040D4FB   cmp         dword ptr [ebp-4],6
0040D4FF   jne         main+68h (0040d508)
0040D501   mov         dword ptr [ebp-8],6
12:
13:
14:       switch(x)
15:       {
0040D508   mov         eax,dword ptr [ebp-4]
0040D50B   mov         dword ptr [ebp-0Ch],eax
0040D50E   cmp         dword ptr [ebp-0Ch],6
0040D512   ja          $L286+7 (0040d549)
0040D514   mov         ecx,dword ptr [ebp-0Ch]
0040D517   jmp         dword ptr [ecx*4+40D552h]
16:       case 0 : y = 0; break;
0040D51E   mov         dword ptr [ebp-8],0
0040D525   jmp         $L286+7 (0040d549)
17:       case 1 : y = 1; break;
0040D527   mov         dword ptr [ebp-8],1
0040D52E   jmp         $L286+7 (0040d549)
18:       case 4 : y = 4; break;
0040D530   mov         dword ptr [ebp-8],4
0040D537   jmp         $L286+7 (0040d549)
19:       case 5 : y = 5; break;
0040D539   mov         dword ptr [ebp-8],5
0040D540   jmp         $L286+7 (0040d549)
20:       case 6 : y = 6; break;
0040D542   mov         dword ptr [ebp-8],6
21:
22:       }

막연히 switch 가 if 문과 동일하지 않을까 생각했었는데 그게 아니었군요..

만드루의 이미지

해당 구문은 Case 수가 적어서 별도로 최적화하지 않은겁니다. case가 10개를 넘어가면 다음처럼 최적화를 합니다.
https://godbolt.org/z/cb6T9zMvM

doldori의 이미지

많은 구현체들이 switch 문을 jump table로 구현하기 때문에 switch 문이 더 좋은
성능을 나타낼 때가 많습니다. 이 경우 레이블은 goto와 비슷한 성격을 갖게 되고요.
Duff's device는 이런 특성을 이용해서 최적화를 한 유명한 예입니다.

seungrye의 이미지

if문이 3개 일때까지는 더 빠르다고 들었었습니다.

정확한 출처를 밝히라고 그러면.. 할말은 없습니다.

예전에 인터넷 어디선가 주워듣기로는 if문이 3개 이하라면
if문이 switch보다 더 빠르다고 들었습니다.

keizie의 이미지

wiki:ZeroAsNull 처럼 정리할만한 내용이라고 생각합니다. 상당히 자주 거론되는 건데도 매번 이런저런 얘기만 듣고 뚜렷하게 정리해서 이해하지를 못하고 있네요.

lifthrasiir의 이미지

switch 문의 최적화는 당연히 컴파일러마다 제각기 다르기 때문에 컴파일러의 특성을 모르고 성급한 최적화를 하는 건 별로 바람직하지 않습니다. (jump table을 쓰는 경우에도 어떤 경우에 쓰고 어떤 경우에 안 쓰고 하는 기준이 다 지 맘대로입니다) 예를 들어서 제 기억이 맞다면 Visual C++는 300인가 그 이상의 값부터는 테이블을 안 쓴다던지 하는 얘기를 들은 것 같습니다. -_-

개인적으로는 연속된 값이 많이 나올 때 (특히 0에 가까운 수부터 시작할 때) switch를 선호합니다만 이건 취향이니 잘 모르겠습니다. 컴파일러랑 상관 없이 빠른 속도를 원한다면 직접 테이블을 만들어서 연결하는 방법이 가장 좋을 것이라 생각합니다. (call stack이 부담스럽다면 gcc extension 같은 걸 써서 라벨의 주소를 저장하는 방법도 있겠죠... ㅂㅌ is everywhere.)

seungrye wrote:
if문이 3개 일때까지는 더 빠르다고 들었었습니다.

정확한 출처를 밝히라고 그러면.. 할말은 없습니다.

예전에 인터넷 어디선가 주워듣기로는 if문이 3개 이하라면
if문이 switch보다 더 빠르다고 들었습니다.

제가 본 대부분의 switch 구현은 대충 이런 식으로 생겼습니다. 이 경우 0부터 N-1까지 case가 잡혀 있는 경우죠. (네 저 GAS 못 씁니다 -_-)

mov ecx, value
cmp ecx, N         ; value가 N보다 크거나 같은 지 확인해서
jae _default       ; 테이블 범위를 나가면 default로 이동
jmp [ecx*4+_table] ; 테이블 안에 들어 가는 값이면 바로 이동

정확하게 기억은 안 나는데 x86의 경우 6클럭 아래로 소요될 겁니다. if가 3개 이하라면 switch보다 더 좋을 가능성이 높겠죠. 하지만 어디까지나 최적화 방법에 따라서 달라지므로 가급적이면 속도보다 가독성을 우선시해야 겠죠.

- 토끼군

sypark33의 이미지

switch문이 Jump table을 하는군요. 그렇다면 case 문이 작은 경우에는 jump table 구성하는데에 대한 오버헤드가 있을 수 있겠구요..

doldori wrote:
많은 구현체들이 switch 문을 jump table로 구현하기 때문에 switch 문이 더 좋은
성능을 나타낼 때가 많습니다. 이 경우 레이블은 goto와 비슷한 성격을 갖게 되고요.
Duff's device는 이런 특성을 이용해서 최적화를 한 유명한 예입니다.
notexist의 이미지

switch문이 항상 jump table로 구현되는 것도 아니고...컴파일러에 따라 코드 상황에 따라 다릅니다.
저 정도 차이는 대부분의 PC에서 크리티컬한 성능의 차이를 보이지도 않고요..
성능보다는 의미를 생각해서 switch와 if를 구분해서 쓰는 것이 좋습니다.
그리고 보통은 switch쪽이 if보다 더 많은 의미를 가지고 있고요.
그렇게 생각한다면 switch가 적합할거처럼 보이는 곳은 switch로 하는 것이 좋습니다.
예를 들자면 하나의 상태변수가 있고 이 값에 따라 다르게 동작하고 상태가 true/false가 아닌 다양한 값을 가지는 경우는 switch로 쓰는게 맞겠죠.

sypark33 wrote:
switch문이 Jump table을 하는군요. 그렇다면 case 문이 작은 경우에는 jump table 구성하는데에 대한 오버헤드가 있을 수 있겠구요..

doldori wrote:
많은 구현체들이 switch 문을 jump table로 구현하기 때문에 switch 문이 더 좋은
성능을 나타낼 때가 많습니다. 이 경우 레이블은 goto와 비슷한 성격을 갖게 되고요.
Duff's device는 이런 특성을 이용해서 최적화를 한 유명한 예입니다.

There is more than one way to do it...

kewlbear의 이미지

효율에 신경이 쓰여서 if/switch를 선택한다는 것은 너무 극단적인 것 같습니다. 비록 if/switch가 메인 이벤트 루프에서 사용되더라도 if/switch의 차이보다는 다른 문제가 비효율의 주범일 가능성이 높다고 생각합니다.

brain2012의 이미지


어느 아키텍처마다 파이프라인을 사용하겠지만

RISC아키텍쳐의 경우는 if 같은 분기문으로 인하여 파이프라인이

깨어질경우 낭비되는 클럭이 40클럭 이상 되는경우도 있습니다.

그런걸 생각하면 결코 극단적이라고 말하긴 힘들거같은데요

========================================================

지하에서 땅파던 삽질마왕 지상에 출몰하다! ( ^-_-^)

foo의 이미지

대게 if문을 쓰게 되고, 3개 이상의 분기가 있을때 switch문을 보통 씁니다만,
저도 tokigun님처럼 가독성에 중점을 둡니다.

if을 쓰게 되느냐 switch문을 쓰게되느냐에 따라 나타나는 성능차이는 전체 프로그래밍 성능에 1%영향 미만일것이라 생각되는군요.

컴파일러 specific한 부분을 논외로 한다고 하면, 위에서 언급된것처럼 switch는 jump문인 것이고, if문보다 일반적으로 효율이 떨어집니다.

if문은 그 판단식이 복잡한 경우에 유용하고, switch문은 판단식이 단순할 경우 유용하고요.

판단식을 최대한 단순하게, if문을 줄이는 방향으로, 판단식이 매우 단순하고 if문을 줄이기 힘든 경우는 switch문으로

shji의 이미지

저도 컴파일러에 따라 어느 것이 좋은지가 달라진다고 생각합니다..
같을 수도 있구요..

아주 동작 시간에 민감한 경우가 아니면 이것으로 고민할 필요는 없을 것
같은데요.. 차이가 나도 많이 나진 않을테니까요..

같은 컴파일러라도 최적화 옵션에 따라 달라질지는 잘 모르겠습니다.

rainmon의 이미지

switch보다 if else를 선호하지만 switch문이 종종 유용하게
사용되는 경우가 있습니다.

예를 들어 어떤 사건(이벤트)에 따라 상태(플래그)값이 변경되고
그 상태값에따라 수행해야 할 로직이 다르다면 보통은 if문을 쓰게 될겁니다.

/* 일반적인 코딩 방식 */
int flag = 0;
...
flag = get_current_status(stdin);

if(flag == 0)
    printf("%s", "stop");
else if(flag == 1)
    printf("%s", "play");
else if(flag == 2)
    printf("%s", "pause");
else
    ;

그런데 만약 상태값이 0일때 1, 2 상태의 로직도 수행해야 하고
상태값이 1 일때는 2 상태의 로직이 수행되어야 할 경우가 있습니다.

이런 비슷한 상황때문에 패턴이란게 있긴하지만 트릭을 쓰자면
switch문의 제어특성을 사용합니다.

switch(flag)
{
    case 0:
        printf("%s", "stop");
    case 1:
        printf("%s", "play");
    case 2:
        printf("%s", "pause");
        break;
    default:
        break;
}

만약 사용하는 언어에선 case문에 break를
반드시 사용해야 한다면 break 대신 goto문을 사용하면 됩니다.
ed.netdiver의 이미지

compiler에 의한 jump table에 의미를 두기보다는 code level에서 lookup table구축이 필요한 경우에 switch를 쓰고 있습니다.
if는 procedure의 sequence가 이어질 경우에 사용하고,
switch는 불규칙 bound가 발생하는 경우에 사용합니다.
대개 state machine의 경우인데, state간의 명확성이 모호해지는 것을 막기 위해 fall through는 안씁니다. 그럴바엔 그냥 한 state 내에 두는 편이 옳다고 생각합니다.

--------------------------------------------------------------------------------
\(´∇`)ノ \(´∇`)ノ \(´∇`)ノ \(´∇`)ノ
def ed():neTdiVeR in range(thEeArTh)

익명 사용자의 이미지

ed. wrote:
compiler에 의한 jump table에 의미를 두기보다는 code level에서 lookup table구축이 필요한 경우에 switch를 쓰고 있습니다.
if는 procedure의 sequence가 이어질 경우에 사용하고,
switch는 불규칙 bound가 발생하는 경우에 사용합니다.
대개 state machine의 경우인데, state간의 명확성이 모호해지는 것을 막기 위해 fall through는 안씁니다. 그럴바엔 그냥 한 state 내에 두는 편이 옳다고 생각합니다.

lookup table 구축이란 말과 한 state 내에 두는 편이 옳다는 말이 무슨 뜻인가요?
이곳의 많은 분들은 이해하셨겠지만..

죠커의 이미지

저는 jump table을 의도하고 switch를 쓰는 일은 매우 어리석은 방법이라고 생각합니다. 그런 의도를 가졌으면 토끼군님이 글을 적으신 것 처럼 명시적으로 만드는 것이 올바릅니다.

명백한 의도를 가지고 있으면서 컴파일러의 최적화에 맞기는 우연에 맞기는 프로그램을 하는 것은 바른 자세가 아니라고 봅니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.