[정보]
c언어; 장문의 if else switch case 없애는 방법
루비(218.147)
2020-04-27 22:53
추천 1
이 방법은 사람들 뻔히 다 아는 고전적인 방법인데... 혹시 모르는 사람들은 꼭 볼 것!!!!
함수 포인터 배열과 enum (혹은 #define), char *message[] = {..., ..., ..., ...}; 이것들 엄청 유용함.
예제에서는 if 를 4개만 썼는데, if 가 수십개 넘어가면 아래 방법을 써보삼. 속도도 빠름.
아래를 보삼~~~~~~
#include <stdio.h>
#include <string.h>
enum {
STATE0
,
STATE1
,
STATE2
,
STATE3
};
void example1
()
{
int state
= 3;
if (state
== STATE0
)
puts
("state0");
else if (state
== STATE1
)
puts
("state1");
else if (state
== STATE2
)
puts
("state2");
else if (state
== STATE3
)
puts
("state3");
}
void state0
() { puts
("FUNC_CALL[0]"); }
void state1
() { puts
("FUNC_CALL[1]"); }
void state2
() { puts
("FUNC_CALL[2]"); }
void state3
() { puts
("FUNC_CALL[3]"); }
static void (* FUNC_CALL
[4]) () = { state0
, state1
, state2
, state3
};
void example2
()
{
int state
= 3;
FUNC_CALL
[state
] ();
}
void example3
()
{
const char *str
= "state3";
if (!strcmp
(str
, "state0"))
puts
("state0");
else if (!strcmp
(str
, "state1"))
puts
("state1");
else if (!strcmp
(str
, "state2"))
puts
("state2");
else if (!strcmp
(str
, "state3"))
puts
("state3");
}
enum {
STATE_MSG_0
= 0,
STATE_MSG_1
,
STATE_MSG_2
,
STATE_MSG_3
};
const char *message
[] = {
"state-msg-0",
"state-msg-1",
"state-msg-2",
"state-msg-3"
};
void example4
()
{
int state_msg
= STATE_MSG_3
;
puts
(message
[state_msg
]);
}
int main
(int argc
, char **argv
)
{
example1
();
example2
();
example3
();
example4
();
return 0;
}
인간적으로 switch case로 되는거에 함수 포인터 쓰진 말자 ㅇㅅㅇ
함수만 수백개 ㄷㄷㄷㄷㄷㄷ
가독성은 둘째치고 성능 반갈죽 될것같은데
strcmp, 라던가... 비교 문에 뭐가 있냐에 따라서 어떻게 될 지 모르지
그리고 swich case 문이 수천개 되면, 컴파일하는데 수십분 걸림. 예전에 그 문제 때문에 배열 인덱스로 참조하는 방식으로 변경했지. 실제 써본 코드임. 빠름.
비교문 말하는거 아니고 far jump랑 near jump랑 다르다고
함수 수백개 만드는건 안느리고?
(컴파일이)
case 문은 벤치함 돌려봐야겠다
점프 거리도 그렇고 일단 함수 호출하면 최소 레지스터 싹 비워지고 메모리 접근 몇번씩 할텐데 비교가 안되지
124는 애초에 상태가 정수로 정해진다는 가정하에 된거라 3이랑은 적용범위가 다름 문자열 쓴다고 무슨 컴파일타임 해시니 뭐니 하는건데
그래서 for 문으로 5000 * 5000 돌림.func 는 0부터 4999: 5000개.case-case.c 는 case 4999: func4999() 이런식으로,case-func.c 는 FUNC_CALL[i] ()컴파일 속도는 둘 다 1초 정도 걸림: 최적화 옵션 없음$ cc -o bench-case bench-case.c $ cc -o bench-func bench-func.c$ time ./bench-case 0.86 real 0.86 user 0.00 sys$ time ./bench-func 0.51 real 0.51 user 0.00 sys컴파일 속도는 둘 다 3초 정도 걸림: 최적화 옵션
거봐 함수 포인터 빠르다니까.. 저건 느린게 아냐. 실제 벤치 돌리기전까지는 아무도 몰라.
최적화 옵션 전에는 함수 포인터 배열 방식이 더 빠르고, 최적화 O2 후에는 case 방식이 더 빠르지. 그 결과는 1년 후에 바뀔 수도 있음. 그리고, 함수 포인터, 참조 연산 안 느림. 빠름. 위 벤치는 case 상수: 벤치고, if 에 strcmp 이런게 들어가면... 내가 위에서 말한 방법이 빠르겠지. strcmp 연산이 빠지는데,,, 근데 그것도 벤치 돌려보기 전까진 장담 못하겠다 ㅋㅋㅋ
ㅅㅂ 벤치 돌리다가 정신 팔려서 자정 넘어 버렸네 ㅠㅠ
내 말이 믿어지지 않으면 벤치마크 돌려 보셈^^
ㄺㄴ
일단 example부터 사이드 이팩트 천지고 ㅋㅋㅋ.... ㄺㄴ네
556352 Apr 28 00:19 bench-case 340794 Apr 28 00:15 bench-case.c 507232 Apr 28 00:20 bench-func 162991 Apr 28 00:14 bench-func.c cc -O2 -pipe 470336 Apr 28 00:44 bench-case 507232 Apr 28 00:44 bench-func $ time ./bench-case 0.00 real 0.00 user 0.00 sys
$ time ./bench-func 0.37 real 0.37 user 0.00 sys 위에 올린 글에 짤렸길레, 짤린 부분 올림. 5000 * 5000 = 25,000,000 번 돌린 결과임. 빠름. 느린거 아님!
최적화 안킨벤치는 하나도 의미없고 0초나온 벤치도 의미없음 그거갖고 알수있는건 함수포인터는 최적화되기 아주 힘들다 뿐
참고로 0초대가 아니라 0.00 나온거 말하는거임 측정할 코드가 최적화로 다 날라간 상태
최적화 안 켰을 때, 측정할 코드가 남아 있는거지. 5000개의 case 보다는 함수 포인터 방식이 더 빨랐음. 보십보 백보라 큰 의미 없지만, 함수 포인터는 느린 방식이 아님. 빠름 방식임.
ㄴㄴ 최적화 켜고 코드도 남아있는 상태여야 제대로된 벤치마크임
당장 어떤 코드가 느리다고 어디다 올릴때 최적화 옵션 켜라고 먼저 답변 날라옴 제일 기본적이고 중요한거임
최적화 켜면 남을지 안 남을지 모르지. 컴파일러가 어떻게 최적화할지 모르는데. 그리그 컴파일러의 의존해서 프로그래밍하는건 좋은 습관 아님
네이티브 언어 퍼포먼스 좋은게 90%이상이 그 최적화 덕분인데 그걸 어떻게 떼놓고 말함
함수 포인터 배열도 최적화 해주면 좋을텐데 ㅠㅠ 컴파일러 발전하면 언젠가 기본 옵션으로 최적화되는 일 나오겠지.
벤치코드 말고 위에 O2 코드 최적화 옵션 주고 에셈 뽑아보니까.. example2 함수에서 함수포인터 배열있는데, 그거 최적화된다. cc -S -O2 example.c 이렇게 하면 됨. 즉, 내가 하고 싶은 얘기는 함수 포인터는 개느리지 않다는 거. 그게 최적화 때문에 다른거에 비해서 개느린것 처럼 보일 수도 있겠지만, 다소 약간 느릴 수도 있겠지만, 빠른 거임. 그리고 위 example.c 코드는 코드가 짧아서 그런지 최적화 잘 되네. 직접 벤치 돌리고 어셈 코드 뽑아보는거 아니면 아무도 장담할 수 없는겨. 그리고 코드가 짧아서 최적화 옵션 넣고 벤치 돌리는 것도 의미없구. 그리고 참조 연산 신경쓸 정도면 어셈으로 코딩해야지. 참조 연산이 얼마나 빠른 연산인데.
참조 연산이 다른 연산보다 느린 축에 속하지는 몰라도, 어플 속도를 향상시키기 위해서는 알고리즘, 자료구조를 손질하는데 더 도움됨. 엥간한 연산들 굉장히 빠른데, 포인터참조, 배열 이런 거 없애면서 평면적으로 프로그래밍을 할 수는 없는거 아님. 주로 입출력IO 부분, 자료구조, 루프 이런 거 손질하는게 어플 속도 향상에는 큰 도움된다는 얘기. 네트웤 어플치고 socket, select, poll 같은거 안 쓰는 어플 찾기 어려운데, 그런 부분을 손보는게 속도 향상에 큰 도움됨. 이벤트 처리 루틴이런가. 느린 부분만 꼭 찝어서 그 부분만 보고 고쳐야 하는거지.