2013년 12월 16일 월요일

임베디드 프로그래밍 C코드 최적화 - 1


임베디드 프로그래밍 C코드 최적화를 읽고 정리...



임베디드 환경은 리소스가 부족하다.

여기서 리소스라 하면 메모리가 될수도 있고, CPU 성능이 될 수도 있고, 디스크의 남은 용량도 될수 있다.(요즘에는 라즈베리파이, 오드로이드 등등 조그만 고성능 보드들이 저가에 나오고 있지만 아직은 바로 바꿀것 같진 않다.)

그래서 메모리가 부족하거나, 디스크(롬)의 남은 용량이 부족하거나 하는 상황에 마주치고는 하는데 이를 위해 최적화가 필요한 것이다. x86이라면 굳이 해주지 않아도 될 정도의 리소스를 가지고 있어 굳이 최적화가 필요없다.


최적화의 방법들

1. 변수를 남발하기보다는 비트연산을 사용하자.
2. 포인터 없이 메모리에 접근할 수 있다.

 1
2
3
4
5
6
7
8
9
10
#define PA  (*(volatile unsigned char *)0x30000000)
int main (void)
{
    PA |= (0x7 << 5);
    return 0;
}

3. 컴파일러를 너무 믿지 말고 volatile 키워드를 쓰자.
    컴파일러가 똑똑하지만 우리는 컴파일러에게 속고 있을 수도 있다.
    volatile의 기능적 의미는 캐시사용안함이다. 보통 프로그램이 실행될 때 속도를 위해 필요한 데이터를 메모리에서 직접 읽어오지 않고 캐시로부터 읽어온다. 하지만, 하드웨어에 의해서 변경되는 값들은 캐시에 즉각적으로 반영되지 않으므로 데이터를 캐시로부터 읽어오지 말고 주 메모리에서 직접 읽어오도록 해야한다. 이러한 특성 때문에 하드웨어가 사용하는 메모리는 volatile로 선언해야 하드웨어에 의해 변경된 값들이 프로그램에 제대로 반영된다. - 

   이 키워드는 멀티코어 프로그래밍에서도 중요하게 사용되니 잊지 말자

4. 개발툴을 잘 이해하라.
   함수 호출 시 인자를 몇 개로 지정하는 것이 효과적인가는 컴파일러마다 다르므로 컴파일러를 잘 이해하는 것이 중요하다.


5. 포인터 체인을 제거하라

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Point
{
    int x,y,z;
};
struct Obj
{
    Point *p1, *d;
};
void draw(struct Obj *a)
{
    a->p1->x = 0;
    a->p1->y = 0;
    a->p1->z = 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Point
{
    int x,y,z;
  
};
struct Obj
{
    Point *p1, *d;
};
void draw(struct Obj * a)
{
    struct Point *k = a->p1;
    k->x = 0;
    k->y = 0;
    k->z = 0;
}
위쪽의 코드는 a를 접근할 때 매번 a를 다시 읽어오기 때문에 성능의 저하가 일어난다는 것이다.

6. register 변수를 활용해라.

7. 적절한 데이터 타입을 선택해라.
   데이터 버스는 데이터가 이동하는 통로이므로 데이터 버스의 폭은 프로세서에서 데이터를 한 번에 읽어오는 양과 직접적인 관계가 있다. 32비트 프로세서의 경우 데이터 버스의 폭이 4바이트이다. 이 프로세서에서 double형의 데이터를 메모리로부터 읽어오려면, double 데이터 타입의 크기가 8바이트이므로 4바이트의 버스 폭으로 데이터를 가져오려면 두번의 액세스가 필요하다. 그럼, 4바이트보다 작은 데이터 타입을 읽을때도 마찬가지로 효율성이 떨어진다. - 150P


http://kldp.org/node/79109
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/C/Documents/COptimization
를 참고하면 더 좋은 이야기를 들을 수 있을 것이다.


댓글 없음:

댓글 쓰기