안녕하세요.

 

 

최근에 엄청난 속도 최적화를 해야 될 일이 있어서 몇달간 고생을 조금 하였습니다.

 

STM32H7 하에서 이루어진 작업이므로 모든 프로젝트에 적용하기는 어렵습니다만,

거의 공통적인 부분이라 ARM 계열은 최소한 비슷하리라 생각됩니다.

 

너무 뻔한 내용일 수도 있으니 병아리들만 보셔도 좋습니다.

 

일단 어떤 기법보다 확인하는 툴이 중요하겠지요.

 

[Tool]

1. Diassembly 디버깅

  1) 컴파일러 툴을 이용하여 디어셈블하면서 내 코드가 어떻게 변환되는지 확인한다.

  2) 어셈블리 코드를 몰라도 되며 몇줄로 바뀌는지만 확인하면 충분하다.

 

2. CPU Counter 레지스터가 있으면 원하는 함수 시작부터 마칠때까지 CPU 카운트를 확인하여

    몇 카운트가 흘렀는지를 확인하여 수치로 정확하게 확인해본다.

 

도구가 준비되었으면 이제는 내 머리가 준비되면 됩니다.

[기본 지식]

1. 속도와 용량 둘다 잡을 수 없다. 싸고 좋은 물건은 없다.

   속도가 빠르면 용량이 크고 , 용량이 좋으면 속도가 느리다.

 

2. 위 1번 툴을 이용하여 C 코드가 어떻게 변경되는지 감을 조금 잡는다.

 

-아래부터는 STM32 쪽

3. 라이브러리는 끝까지 추적해서 직접 제어 하도록 한다.

    STM32 의 경우 HAL 라이브러리는 되도록 자제하고 직접 레지스터를 제어한다.

    그렇다고 무식하게 번지를 그냥 입력해서 가독성이 떨어지도록 할 필요는 없다.

 

4. 컴파일러 옵션은 speed 최대

 

5. 시스템의 캐쉬를 켤수 있다면 On ( 7-10배 증가)

 

6. DTCM 과 ITCM 이해

 

7. Bus 아키텍처를 보고 가장 짧은 라인으로 데이터가 움직이도록 처리

 

8. 되도록 DMA 처리

 

9. flash 보다는 ram 에서 실행하는게 더 빠르겠지요.

 

 

 

 


거의 저의 경험이긴 한데 많은 도움이 되실겁니다.
아시는 분은 당연하지만 초보자들은 아마 헷갈리거나 모르시는 분들이 많을 거예요.

혹시나 제가 실수할 수도 있으니 잘못된 부분있으면 조언도 같이 부탁드려요~~

1. string 에서 마지막은 NULL 이고 실체가 있는 공간이다.
char buf[16];
sprintf(buf,"%s","hello");
여기에서 strlen(buf) 은 5이겠지요. 하지만 buf 를 차지하고 있는 공간은 6바이트입니다.
스트링에서 마지막에 항상 NULL 이 붙지요.

그래서 이런 실수를 합니다.
char buf[4];
char value = 5;
sprintf(buf,"%s","abcd”);
printf(“%d”, value);

이걸하고 나면 아마도 value 값이 0 이 됩니다. 이걸 처음본 뉴비들은 메모리 고장났다면서 컴퓨터에 대한 신뢰가
확 떨어지게 되지요. 하지만 당연한 일이기도 합니다.

제가 프로그램시 제일 좋아하는 격언이지요.

"아니 땐 굴뚝에 연기나랴"

만약, 아니땐 굴뚝에 연기가 난다면, 새벽에 굴뚝안에 맺혀진 이슬로 인해 낮에 온도가 오르면서 수증기로 변해서
불을 때지 않는데도 굴뚝에 연기가 나는 것으로 오인할 수 있는 것처럼 모든 일에는 원인이 확실히 있습니다.


2. memcpy 와 memmove 차이점 사용법 이해
- 메모리 간 복사를 위해서는 memcpy 사용하는데 단 하나의 제약이 있다면 자신으로 복사를 못한다는 겁니다.
더 정확히 말하면 의도치 않은 결과가 나옵니다. 대신에 memmove 를 사용해야지요.
- 아래는 컴파일시 에러가 없지요. 하지만 결과는 예상과 다르게 나옵니다.
char buf[24];
memcpy(buf+4, buf, 16);
--> memmove(buf+4, buf, 16);


- memcpy 구현 소스를 보신다면 왜 잘 못동작할지 이해가 될껍니다. memmove 소스랑 비교해보세요.
void *memcpy (void *dest, const void *src, size_t len)
{
char *d = dest;
const char *s = src;
while (len--)
*d++ = *s++;
return dest;
}
void *memmove (void *dest, const void *src, size_t len)
{
char *d = dest;
const char *s = src;
if (d < s)
while (len--)
*d++ = *s++;
else
{
char *lasts = s + (len-1);
char *lastd = d + (len-1);
while (len--)
*lastd-- = *lasts--;
}
return dest;
}

- 참고로 단순하게 모든 memcpy 대신에 안전하게 mommove 를 사용해도 됩니다만 그렇게 되면 발생하는 문제가 있지요. 이건 숙제!

- 한개더 memcpy 는 자기 자신을 복사못한다고만 알고있으면 안됩니다. 아래를 보세요. 이거는 잘동작해요.
memcpy(buf, buf+4, 16);


3. struct 를 sizeof 시 길이는 내부의 변수의 합과 다르다.
아래 참조.
https://www.geeksforgeeks.org/is-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member/
생각대로 1바이트 단위로 묶는 방법도 있다. 숙제!

4. C 에도 버전이 있고 문법이 다르다. 하지만 매우 큰차이는 없다.
ansi, c89 , c90 , c99, c11
애플의 swift 2,3,4,5 처럼 예전 코드가 컴파일 안되고 그러는 무지막지한 변화는 아니지만 차이는 알고 있어야 한다.
C 도 하위 호환이 안되는 문법을 사용하면 당연히 컴파일 에러가 발생한다.
그래서 이식성을 극도로 추구한다면 ansi 문법으로 작성해야 한다.

5. CPU 의 메모리 저장방식에는 little endian 과 big endian 이 있으며 , 메모리 복사시에 주의하여야 한다.
이것도 정확히 알고 넘어가야 하는 부분입니다. 이게 있는것 조차 모르시는 분들도 있더라구요.

요기까지 하겠습니다~~

+ Recent posts