2004년 03월 31일
[C/C++] 너희가 switch 를 아느냐?
switch 는 C/C++, Java, C#, J++, J# 등에서 널리 쓰이는 조건 분기문이다.
다른 언어에서도 비슷한 기능의 조건 분기문이 존재한다.
과연 C/C++ 의 switch 구문은 어떤 방식으로 작동할까?
흔히 알려진 바로는 switch 구문이 if/else 비교 없이 해당 case 로 곧장 jump 하기 때문에 속도가 빠르다고 한다. 이것은 부분적으로 사실이다. 컴파일러가 이런 방식을 사용하게 된 것은 DOS 시절의 WATCOM C 부터였다고 한다.
이에 대한 해답은. 바로 '조합' 이다.
예>
case 0, 1, 2 -> if/else 로 분기 (규칙 1)
case 0, 1, 2, 3, 4 -> Address table 로 분기 (규칙2)
case 0, 1, 2, 3, 4, 100 -> Address table 로 분기 (규칙 2)
case 0, 1, 2, 3, 4, 100, 300 -> 0~100 까지는 Address table 로 분기. 300 은 if/else 로 분기(규칙3)
case 0, 300, 301, 302, 303, 304 -> 모두 if/else 로 분기 (규칙3)
이 외에도 몇 가지 규칙이 더 있을것으로 보인다. 하지만 이것 만으로도 벌써 switch 구문을 효과적으로 활용하기 위한 제한사항들이 머리속에 마구 떠오를 것이다. (5번째 예와 같이 구성해서는 안될것이다)
다른 언어에서도 비슷한 기능의 조건 분기문이 존재한다.
C/C++ 에서는 그 제약이 크다. switch 구문에서는 반드시 정수(int)만 비교할 수 있고 분기하는 case 에는 상수(constant value)만 넣을 수 있다. (다른 고급언어들은 부동소수점, 문자열 심지어는 사용자 정의 데이터 타입 까지 비교할 수 있다) 이 제약은 고속 스위칭(switching)을 위해 존재하는 것이다.
과연 C/C++ 의 switch 구문은 어떤 방식으로 작동할까?
흔히 알려진 바로는 switch 구문이 if/else 비교 없이 해당 case 로 곧장 jump 하기 때문에 속도가 빠르다고 한다. 이것은 부분적으로 사실이다. 컴파일러가 이런 방식을 사용하게 된 것은 DOS 시절의 WATCOM C 부터였다고 한다.
프로그래머라면 생각해보자. 비교할 값만으로 jump 하려면 Address table 이 필요하다. 과연 Address table 만으로 switch 구문을 비교 없이 jump 하도록 구성할 수 있을까? 오래 생각하지 말자. 당연히 불가능하다.
32bit 플랫폼에서 int 는 4Giga 의 범위를 갖는다. Address point 도 4bytes 일 경우 Address table 은 최대 16GB 의 메모리를 사용할 수도 있다. (case 0, case 0xFFFFFFFF)
이에 대한 해답은. 바로 '조합' 이다.
즉, 연속적이거나 집적된 case 의 경우에는 해당 범위만큼의 테이블을 만들어 jump 하고 듬성듬성 떨어져 있는 case 의 경우는 if/else 를 이용하여 case 수 만큼 비교후 분기하는 것이다.
VC++ 6.0 과 7.0 의 디스어셈블리 기능을 이용해 분석해본바에 의하면 switch 구문은 다음과 같은 규칙을 갖고 기계어를 생성한다.
1. case 가 3개 이하인 경우에는 if/else 로 일일히 비교후 분기 한다.
2. case 가 4개 이상이고, 최소 case 와 최대 case 의 차이가 254 이하인 경우에는 Address table 을 만들어 jump 한다. (이 과정에서 최대 약 1KB 의 Address table 이 생성된다. switch 를 잘못 쓰면 1KB 의 디스크 공간과 메모리를 낭비할 수도 있다)
3. 최소 case 와 최대 case 의 차이가 255 이상인 경우에는 if/else 로 비교후 분기한다. 단, 최소 case 를 포함하며 규칙2의 조건을 만족하는 부분집합이 존재할 경우 해당 case 들은 Address table 을 만들어 jump 한다.
2. case 가 4개 이상이고, 최소 case 와 최대 case 의 차이가 254 이하인 경우에는 Address table 을 만들어 jump 한다. (이 과정에서 최대 약 1KB 의 Address table 이 생성된다. switch 를 잘못 쓰면 1KB 의 디스크 공간과 메모리를 낭비할 수도 있다)
3. 최소 case 와 최대 case 의 차이가 255 이상인 경우에는 if/else 로 비교후 분기한다. 단, 최소 case 를 포함하며 규칙2의 조건을 만족하는 부분집합이 존재할 경우 해당 case 들은 Address table 을 만들어 jump 한다.
예>
case 0, 1, 2 -> if/else 로 분기 (규칙 1)
case 0, 1, 2, 3, 4 -> Address table 로 분기 (규칙2)
case 0, 1, 2, 3, 4, 100 -> Address table 로 분기 (규칙 2)
case 0, 1, 2, 3, 4, 100, 300 -> 0~100 까지는 Address table 로 분기. 300 은 if/else 로 분기(규칙3)
case 0, 300, 301, 302, 303, 304 -> 모두 if/else 로 분기 (규칙3)
이 외에도 몇 가지 규칙이 더 있을것으로 보인다. 하지만 이것 만으로도 벌써 switch 구문을 효과적으로 활용하기 위한 제한사항들이 머리속에 마구 떠오를 것이다. (5번째 예와 같이 구성해서는 안될것이다)
이런 작은 차이로부터 우리는 명품을 끌어낸다.
# by | 2004/03/31 18:04 | 프로그래머 생각 | 트랙백 | 덧글(7)





☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
평소 잘 모르고 쓰던건데...
이런 내용은 어떻게 공부를 하신건가요?
전 공부를 대충해서, 잘... ㅠ.ㅠ
앞으로 자주 들를께요. ^^
00401760 cmp eax,4
00401763 ja 004017C5
00401765 jmp dword ptr [eax*4+4017CCh]
하지만 경우에 따라선 평균 속도를 깎아먹을 수도 있겠네여...^^ 간접 주소 분기 명령이 클럭을 꽤 소비하거든요...
암튼 앞으로도 한동안은 어셈블리 쓰는걸 포기할 순 없겠습니다...
좋은 정보 감사합니다...^^
퍼갈께요~ ^^
http://www.hbkr.net/board/zboard.php?id=dev_forum&page=1&page_num=20&select_arrange=headnum&desc=&sn=off&ss=on&sc=on&keyword=&no=28&category=1