◎위챗 : speedseoul
간혹, C언어를 잘 사용하는 사람들은 자신만의 고유한 역량인 듯, 최적화라는 이름하에 코드를 읽기 어렵거나, 구조적이지 않게 작성하는 경향이 있다. "좋은 코드는 읽기 쉬운 코드"라는 것은 진리다. 즉, 어렵게 코드를 최적화 시키려고 노력하기 보다는, 읽기 쉬운 코드를 작성하려고 시간을 투자하는 편이 도움이 된다. 아래는 최적화를 위한 몇 가지 원칙을 나열한 것이다.
1. 최적화를 위해서 코딩하지 말라.
2. 정말 최적화가 필요한지 검토하라.
3. 최적화하기에 앞서 측정하라.
4. 성능이 문제가 되는 부분만 최적화 하라.
최적화하기 위해서 코딩했다고 하는 대부분의 코드는 가독성이 떨어지거나(코딩한 사람만 알아보거나), 실제로 최적화 되지 않은 경우가 많다. 물론, 코딩룰에서 일부분에 대해서 최적화를 위한 방법으로 정한다면 이야기는 조금 달라지지만, 최적화를 위해서 코딩하는 것은 유지보수나 수정비용만 높일 뿐이다. 최적화 때문에 계층화나 추상화와 같은 개념이 생략되고, 의존성이 높아지는 코드를 만들게 되면, 정말 최적화나 구조변경이 필요한 순간에는 도움이 되지 못할 뿐더라, 심한 경우에는 걸림돌로 작용하는 경우가 많다.
정말 최적화가 필요한지를 따져물어야 한다. 충분히 코드를 빠르게 실행되고 있고 공간도 적게 차지한다면, 굳이 최적화를 할 필요가 없을 수도 있다. 임베디드 시스템과 같은 경우 실제로 최적화를 해야한다고 이야기하지만, CPU의 노는 시간이(Idle Time) 70%이상을 차지하는 경우도 종종있다. 이것은 대부분 I/O에 관련된 대기시간 때문에 발생하는 것이지, 코드를 최적화해서 수행속도를 높여야 할 이유는 되지 않는다. 극한의 최적화가 필요한 순간도 있지만, 사실 그 정도쯤 되면 다음번 제품에선 다른 CPU를 사용하거나, RAM을 늘려야 할지도 모른다. 최적화로 인해서 발생하는 복잡도 증가가 차기 제품에까지 영향을 주지 않도록 조심해야 할 것이다.
최적화를 하기위해서는 어느 곳을 최적화 해야할지를 정해야 한다. 예를 들어, 100번 수행하는 함수 1000초가 걸린다고 할 때, 전체적으로 100,000초를 사용한다. 5번 사용하는 함수가 10,000를 사용한다면 50,000초를 사용한다. 어떤 부분을 최적화하는 것이 더 좋을까? 당연히 더 자주 사용되는 함수가 빠른 것이 전체적인 시스템의 성능을 개선할 가능성이 높다. 측정하지 않는다면 정확히 어디를 최적화 해야할지 알 수 없다. 그런 상태에서 최적화를 위해서 시간을 투자한다는 것은 밑빠진 독에 물 붓기와 다르지 않다.
컴파일러의 최적화 옵션을 프로젝트의 후반에 높이는 것은 위험하다. 갑자기 잘 동작하던 코드들이 제대로 동작하지 않을수도 있다. 따라서, 컴파일러의 최적화 옵션은 가능한 과제의 초기부터 높이 설정하는 것이 좋다. 예를 들어, "GCC"와 같은 경우에는 "-o3" 옵션을 이용해서 "Release"모드로 빌드할 필요가 있다. 만약, 특정 CPU를 사용하는 임베디드 시스템과 같은 경우에는 컴파일러들도 다 달라지는데, 상업적으로 구매할 수 있는 컴파일러들이 최적화(코드 크기 및 시간적인 부분에서)를 더 잘하는 경향이 있다. ARM프로세서용 프로그램을 개발하는 할 때는 "GCC"보다는 "ARM" 컴파일러가 더 높은 성능을 가져다줄 가능성이 높다.
컴파일러의 최적화 옵션을 제대로 점검하지 않으면, 과제의 후반에 가서는 코드의 분량이 커져서 컴파일러의 최적화 옵션을 제대로 활용하지 못하는 경우도 발생한다. 이때는 최적화 옵션을 적용할 수 있는 부분들을 찾아서 분리시켜서 컴파일하는 것도 고려해 볼 수 있다. 물론, 제대로 하는 것은 초기부터 최적화 옵션을 높이 설정하는 것임을 잊어선 안된다.
1.1 ARM 코드의 최적화를 위한 C코딩
ARM CPU를 사용하는 경우에는 ARM CPU의 특성에 맞출 필요가 있다. 주로 자주 실행되는 코드에 대해서 최적화는 이익을 크게 가져다주는 경향이 있다. 따라서, 코드에서도 순환적으로 반복되는 곳들에 대해서는 최적화를 위해서 코딩을 할 필요가 있다. 물론, 그렇다고 최적화를 한다고 가독성까지 낮추는 일이 있어서는 안된다. 가독성은 유지하면서 최적화를 할 수 있는 방법을 찾아야 한다. 이것이 일반화 되면 코딩 룰이나, 코드 리뷰와 같은 곳에서 체크리스트로 활용할 수 있을 것이다.
http://www.codeproject.com/Articles/6154/Writing-Efficient-C-and-C-Code-Optimization
위는 ARM CPU에서 C코드의 최적화를 잘 다루고 있다고 생각되는 웹 주소다. 이곳에 있는 것들과 ARM에서 제공하는 각종 메뉴얼을 이용해서 C코드의 최적화를 위한 방법을 스스로 익혀나갈 수 있을 것이다. 이곳에서는 몇 가지 중요한 것들을 위주로 살펴보도록 하겠다.
1.1.1 될 수 있으면 모든 변수의 값을 Integer로 선언하라.
Integer연산이 CPU에서는 가장 빠르다. 즉, CPU의 레지스터 크기에 맞는 가장 적합한 크기의 변수가 Integer이다. 따라서, 다른 변수 타입에 비해서 연산 속도가 빠를 수 밖에 없다. 특히, 부호를 가진 Integer보다는 부호가 없는 Integer가 더 빠르다. 부호를 가진 경우에는 부호에 대한 처리를 위해서 하드웨어가 할 일이 더 많아지기 때문이다. 따라서, 가능한 모든 변수는 부호가 없는 Integer가 좋다. 변수의 크기를 최적화하기 위해서 딱 맞는 정도의 변수 타입을 선언해서 쓰는 것은 형변환을 자동으로 발생시킬 가능성이 높으며, 이런 것들이 늘어날 수록 성능은 저하된다.
Floating Point연산의 경우 ARM에서는 기본으로 Library를 통해서 소프트웨어적으로 제공한다. 따라서, Floating Point 변수가 선언되면, 필요한 라이브러리들이 자동으로 더 추가되기에 성능의 저하와 동반해서 크기의 증가도 발생한다. Floating Point가 정말 필요한지를 따져봐야 하고, 만약 정말 필요하다면 오히려 소수점 이하 얼마까지의 정밀도를 가져야 하는지를 검토한 후에, 정수를 곱해서 사용해주는 것이 좋다. 예를 들어, 소수점 이하 2자리까지가 유효하면, 100을 곱해서 정수로 만들어줄 수 있다. 연산 결과의 최종본은 나중에 100으로 다시 나누어 주어야 할 것이다.
[ 참고 ]
GCC를 이용하고 Eclipse에서 개발을 하고 있다면, "Project --> Properties --> C/C++ Build --> Settings --> GCC C Compiler --> Miscellaneous"에 "-Wa,-aln='${CWD}\\${InputFileBaseName}.cod'"를 설정해서 Assembly 코드와 C코드를 같이 보도록 하자.