c_code_optmization

Download Report

Transcript c_code_optmization

C 코드최적화
세명대학교
AI연구실
양승조
1. 소개
• 모바일 프로그래밍
– 일정 수준의 품질을 유지하면서 자원을 덜 차
지하고 빠른 속도로 처리되는 프로그램을 개
발 해야함
• PC에서도 메모리의 용량도 높아지고 속도
도 더욱 더 빨라지고 있으나 역시 최적화
가 필요하다
2. 어디에 필요한가?
• 각각의 모듈 중 어느 모듈이 느리게 작동
하는가? 메모리를 많이 차지하는가?를 알
아내는 것이 중요하다.
• Visual c++
– Profiler (함수별 소비시간 측정)
• 리눅스
– gpof, profiler
• 루프 혹은 third party 라이브러리 매서드
를 호출하는 영역
3. 정수
• 사용해야 할 값이 음수를 사용하지 않는다
면 unsigned int 를 사용해야 한다.
• 연산 시간
• float > int > unsigned int
4. 나눗셈 & 나머지
• 나눗셈 연산은 피하는 것이 좋다.
• a/b > c -> a > b*c
5. Combining division and
remainder
• int func_div_and_mod (int a, int b) {
return (a / b) + (a % b);
}
• 나눗셈 function을 컴파일러에 결합하는
것이 좋다.
– 나눗셈 function은 항상 나눈값과 나머지를 리
턴하기 때문에
6. 2의 배수로 나누기
• 나누기를 할 때 2의 배수를 분자화 함으로
써 효율적으로 만듬
– 나누기 대신 shift를 할 수 있기 때문
– Ex> 66으로 나눠야 한다면 64로 나눌 수 있도
록
• Shift를 하더라도 signed보다 unsigned가
더 오래걸린다.
– 부호비트 때문에 연산이 한번 더 들어감
7. 배열을 이용한 index 생성
if ( queue == 0 )
letter = 'W';
else if ( queue == 1 )
letter = 'S';
else
letter = 'U';
static char *classes="WSU";
letter = classes[queue];
8. 나머지 연산자의 대체
• uint modulo_func1 (uint count)
{
return (++count % 60);
}
uint modulo_func2 (uint count)
{
if (++count >= 60)
count = 0;
return (count);
}
• 위 코드에서 if문이 더욱 빠르다
9. 전역 변수
• 전역 변수의 사용을 줄
여야 한다.
– 전역 변수는 레지스터에
할당할 수 없다.
• 전역 변수의 접근을 줄
이는 코드
int
int
int
int
f(void);
g(void);
h(void);
errs;
void test1(void)
{
errs += f();
errs += g();
errs += h();
}
void test2(void)
{
int localerrs = errs;
localerrs += f();
localerrs += g();
localerrs += h();
errs = localerrs;
}
10. Using Aliases
void func1( int *data )
{
int i;
void func1( int *data )
{
int i;
int localdata;
for(i=0; i<10; i++)
{
anyfunc( *data, i);
}
localdata = *data;
for(i=0; i<10; i++)
{
anyfunc ( localdata, i);
}
}
}
위 코드는 변화가 없는 변수임에도 불구하고 루프문을 통해서 계속 접근하고
있다. 지역변수를 써 줌으로써 해결이 가능하다
11. 지역 변수
•
가능하면 char이나 short를 사용하지 않도록 한다.
– 8비트 혹은 16비트를 할당한 후 남는 비트를 줄이는 작업을 추가로 하기 때문
•
int wordinc (int a)
{
return a + 1;
}
short shortinc (short a)
{
return a + 1;
}
•
•
char charinc (char a)
{
return a + 1;
}
이렇게 32비트로 연산을 하도록 함수를 만들 필요가 있다.
이것은 RISC의 장점과 일맥상통한다.
12. 포인터
• 구조체를 그대로 넘길 경우 구조체의 모든
값을 넘기지 말고 포인터를 넘길 수 있도
록
13. Pointer chains
typedef struct { int x, y, z; } Point3;
typedef struct { Point3 *pos, *direction; } Object;
void InitPos1(Object *p)
{
p->pos->x = 0;
p->pos->y = 0;
p->pos->z = 0;
}
void InitPos2(Object *p)
{
Point3 *pos = p->pos;
pos->x = 0;
pos->y = 0;
pos->z = 0;
}
포인터를 사용할 때 여러 번 그 변수에 접근
하지 말고 지역변수에 넣어 놓은 후 접근하면
레지스터나 캐시를 이용하기 때문에 좀 더
효율적으로 작동한다.
코드가 보기 좋다는 이점도 있다.
15. Binary Breakdown
if(a==1) {
} else if(a==2)
} else if(a==3)
} else if(a==4)
} else if(a==5)
} else if(a==6)
} else if(a==7)
} else if(a==8)
{
}
{
{
{
{
{
{
if(a<=4) {
if(a==1) {
} else if(a==2) {
} else if(a==3) {
} else if(a==4) {
}
}
else
{
if(a==5) {
} else if(a==6) {
} else if(a==7) {
} else if(a==8) {
}
}
이진 트리와 비슷한 형식으로
비교횟수가 현저하게 떨어진다
필요에 따라서 3중 if문으로 만들
수도 있는데 좀 더 빠르게 작동
하겠지만 코드가 보기 어려워
진다.
16. Switch 대신 lookup table 를
사용하라
char * Condition_String1(int condition) {
switch(condition) {
case 0: return "EQ";
case 1: return "NE";
case 2: return "CS";
case 3: return "CC";
case 4: return "MI";
case 5: return "PL";
case 6: return "VS";
char * Condition_String2(int condition) {
case 7: return "VC";
if ((unsigned) condition >= 15) return 0;
case 8: return "HI";
return
case 9: return "LS";
"EQ\0NE\0CS\0CC\0MI\0PL\0VS\0" +
case 10: return "GE";
3 * condition;
default: return 0;
}
}
}
17. 루프
• 많은 경우 루프에서 과다한 시간을 소비하
게 된다. 여러번 실행되는 루프틔 특성상
조그마한 시간의 낭비가 게속 누적되기 때
문이다.
17.1 Loop termination
int fact1_func (int n)
{
int i, fact = 1;
for (i = 1; i <= n; i++)
fact *= i;
return (fact);
}
int fact2_func(int n)
{
int i, fact = 1;
for (i = n; i != 0; i--)
fact *= i;
return (fact);
}
17.2 더욱 빠른 for 문
• 함수는 호출되기 위한 오버헤드가 분명 존
재한다. 그래서 반복문안에 함수 호출을
넣는 것보다 함수 안에서 반복문을 쓰는
것이 좋다.
17.3 Early loop break
• 우리는 흔히 루프가 마지막까지 돌지 않아
도 되는데 브레이크를 시키지 않는 경우가
많다. 브레이크를 사용해 쓸 데 없는 루프
를 없애자.
18. 함수 디자인
• 가능한 인자를 줄여라
– 인자가 4개를 넘어가면 스택을 통해서 넘기기
때문에 그만큼 메모리 접근이 발생한다.
– 구조체의 포인터를 넘기는 법도 가능하고 좋
다.
• __inline키 워드를 이용하면 일종의 매크로
처럼 작용을 하며, 함수가 호출되는 대신
함수의 본체가 직접 치환이 되어 버린다.