C++ 최적화 : 최고 성능을 구현하는 10가지 검증된 기법 본문

Programming/C++

C++ 최적화 : 최고 성능을 구현하는 10가지 검증된 기법

halatha 2019. 9. 26. 16:29

C++은 복잡해서 좋아하지 않는 언어지만, 최적화라는 영역은 프로그래밍 언어에 관계없이 적용되는 영역이 있어서 배울 점이 많을 거라 생각했고, 역자가 나름 유명한 개발자라 읽게 되었다.

책의 박스 부분에 나오는 저자의 경험을 보면 저자가 하드웨어 관련 경력이 있음을 알 수 있고 그 때문에 최적화에 더 관심을 갖게 되었을 거란 추측을 할 수 있다. 하드웨어 관련 업종은 일반적으로 제조업이기 때문에 비용을 절감하는 제품을 만들기 위해 성능 최적화가 매우 중요하기 때문이다.

1장

책 전체의 개괄로 최적화가 왜 필요한지, 기본적인 최적화 방법이 무엇인지, 각 장마다 뭘 설명할지 이야기한다.

2장

하드웨어와 관련된 부분이라 프로그래밍은 나오지 않지만 최적화를 하기 위해 꼭 알아둬야 할 부분이다. 1장에 나와있고 일반적으로도 알려졌듯이 하드웨어 최적화가 가능한 경우라면 소프트웨어 최적화에 비해 간단한 코드로 큰 폭의 성능향상이 가능하다(하지만, 사실 이정도의 성능향상을 얻는다면 이전 코드가 매우 성능이 안 좋았다는 반증일 수도 있다). 2장에서 말하는 하드웨어와 관련된 부분은 미세할 수도 있지만 때에 따라서는 굉장히 유용할 수 있는 지식인데 모르는 경우 누가 알려주지 않는 한 정말 알기가 어려우므로 이런게 있다는 걸 기억해두고 필요할 때 다시 보는 정도로 충분할 거 같다.

3장

1장에서 인용했던 크누스 교수의 말을 다시 인용하며 3장을 소개한다. ‘섣부른' 최적화가 만악의 근원이라고 했지, 최적화가 만악의 근원이라고 하지는 않았다. 즉 크누스 교수는 체계적으로 분석한 후 최적화를 통해 얻을 수 있는 성능 향상의 폭이 크다면 그 부분을 집중해서 봐야 한다고 말했던 거라고 저자는 말한다(이 부분을 90/10의 법칙으로 요약한 후 암달의 법칙을 소개). 이어서 당연하게도 프로파일러를 통해 시간을 측정을 하는 이야기와, 어떻게 성능과 관련된 시간을 추론하고 정확히 측정할 것인지 여러가지 방법을 설명한다.

4장

어떤 언어를 쓰건 상관없이 아마 대부분의 개발자가 가장 많이 다뤄야 하는 문자열에 대해 이야기한다. 버퍼의 동적 할당이나, C의 malloc을 이용한 메모리 할당의 빠른 속도, 일반 loop 대신 iterator를 사용하는 경우의 성능향상등이야 다른 언어에서도 통하는 상식과도 같은 부분이지만, a = a + 'some string'  a += 'some string' 이 C++에서는 서로 다른 연산자로 동작한다는 사실은 처음 알았다(임시 문자열 객체 생성 유무의 차이).

5장

알고리즘 최적화에 관해 설명하는데 소팅을 예로 이야기한다. 일반적으로 인터뷰에서 흔히 퀵소트를 가장 빠른 소팅 알고리즘으로 이야기하곤 하고, 이 부분은 대부분의 지원자들이 잘 대답하지만, 최악의 경우에는 어떤지, 특수한 경우에 더 나은 알고리즘이 없는지로 옮겨가면 많은 지원자들이 대답을 못하곤 한다. 저자도 이런 많이 알려진 이야기부터 시작해 더 고도화된 최적화를 위해 사용할 수 있는 여러가지 알고리즘 기법들 — 캐싱, 힌팅, 경로 최적화 등 — 이 어떤 경우에 사용할 수 있는지를 알려준다.

6장

프로그래밍을 하면서 가장 중요한 부분 중 하나인 메모리 할당을 설명한다. C++ 문법에 관계된 부분들이 좀 있어 모두 이해할 수는 없었지만, 기본적인 부분은 다른 언어에서와 동일했다. 예를 들어 참조가 많은 부분에 일어나는 경우나 loop 외부가 아니라 내부에서 변수를 선언하면 성능이 하락되는 경우가 거기에 해당한다. C의 malloc을 쓰다가 C+의 new만 사용해도 편리함을 느끼는데, 스마트 포인터를 쓰면 아마 훨씬 더 쉽게 프로그래밍을 할 수 있을 거 같지만, 편리함과 성능은 일반적으로 반비례할 가능성이 높다는 걸 생각하고 주의를 기울여야 함을 알 수 있었다.

7장

이 장을 시작하면서 저자가 스스로 말하지만, 문장의 최적화는 큰 이득을 얻기가 힘들다. 명령어를 적게 사용하기 때문에 최적화의 이득도 작고, 컴파일러가 최적화하기 어렵거나, 컴파일러에 따라 결과가 다를 수 있기 때문이다. 그렇지만 여전히 문장 최적화를 무시할 수는 없는데, 특히 표현식은 함부로 변경하면 비지니스 로직의 문제가 발생할 수 있어서 컴파일러가 쉽게 최적화할 수 없기 때문이다. 그 외 가상함수, 인터페이스나 DLL 부분등은 C++나 window specific한 부분이라 자세히 읽지 않았다. 하지만 부동소수점 대신 정수를 사용해야 하는 부분은 어느 언어를 사용하느냐에 관계없이 꼭 기억해야 할 부분이다.

8장

라이브러리 최적화에 대해 설명하는데, 이 장은 최적화뿐만 아니라 설계를 할 때도 유용한 방법을 이야기한다. 일반적으로 개발을 할 때 처음부터 모든 걸 새로 설계하는 일보다는 다른 라이브러리나 API를 이용해 개발을 할 때가 많은데, 이 때 기존의 라이브러리/API를 변경하는 일은 위험성이 크다. 해당 코드를 이용해서 동작하고 있는 다른 개발자/부서의 프로그램/API가 망가질 수 있기 때문인데, 그래서 원하는 기능을 추가하거나 상속을 통해 개발을 하는 게 안전하다(물론 이런 경우에도 clean architecture를 위해 필요한 일들이 있지만 이건 최적화와는 별개 이야기이므로 여기서 더 언급하진 않는다).

9장

std::map의 find() 함수를 시작으로, 각종 C++ library를 이용할 때 어떻게 최적화를 하는지를 자세히 설명한다. 코드 예가 C++이라는 점만 제외하면 프로그래밍 언어와 무관하게 적용할 수 있는 방법이라 누구에게나 도움이 된다.

10장

STL을 이용해 라이브러리를 제대로 이용하는 방법을 이야기한다. Big O notation의 관점에서는 같은 complexity를 갖는 자료구조라고 해도, 실제로 테스트를 해보면 내부 구현의 차이 때문에 속도 차이가 나는 경우가 있다거나, 테스트를 진행할 때 내부 구현의 특성을 감안하지 않으면 실제 성능을 낼 수 없어서 테스트가 왜곡될 수 있다는 점을 알려준다. 마지막에는 boost나 EASTL과 같은 더 다양하거나 개선된 자료구조를 갖는 라이브러리를 알려준다. 좀 더 C++에 특화되긴 했지만 9장과 마찬가지로 프로그래밍 언어에 무관하게 배울 점이 있다.

11장

어떤 작업을 하건 거의 피할 수 없는 file I/O와 관련된 최적화를 자세히 보여준다. 간단한 file read 함수를 만들고, 경우에 따라 어떻게 다른지 실험 결과를 밀리초로 알려주는데, 대부분 stl 내부의 함수를 사용하지만 경우에 따라 5배까지 차이가 나기 때문에 잘 알고 사용하는 게 얼마나 중요한지 실감할 수 있다. 상대적으로 write 함수는 간단하게 마치는데, 최초 함수 자체가 한 줄로 끝나서 별로 바꿀 게 없어 보이는데도 수정 후 큰 차이를 나타낸다(std::endl의 사용 유무에 따른 flush 유무로 인해 발생).

12장

아마 다른 어떤 내용보다 이해하기 힘들 거 같은데 동시성에 대한 이야기는 따로 하나의 책으로 써도 엄청나게 두꺼워질 만큼 다양하고 복잡한 내용을 포함하는 주제이기 때문이다. 저자는 일반적인 동시성에 대한 주제와 C++에 특화된 부분을 같이 섞어 자세히 설명하고 있으며, 그 안에서 피해야 할 부분이나 일반적으로 적용할 수 있는 최적화를 보여주지만 다른 장들에 비해 최적화 자체에 대한 이야기는 적다. C++에서도 Java의 Java Concurrency in Practice에 해당하는 책이 있을지 모르겠는데, 이런 동시성에 특화된 책을 따로 봐야할 거라고 생각한다.

13장

마지막으로 메모리 관리를 설명한다. C++이므로 new, delete 연산자에 대한 자세한 내부 동작을 이야기하고, 고성능을 제공하는 malloc을 대체하는 라이브러리를 알려준다(하지만 경우에 따라 성능 향상은 거의 없을 수도 있고 수십%에 이를 수도 있음). 이어서 operator new를 overload해 메모리 관리를 하는 operator를 사용자가 재정의할 수 있는 방법과 어떤 식으로 최적화할 수 있는지를 다양한 코드를 통해 설명한다.

C++은 매우 기초적인 부분밖에 몰라 코드를 자세하기 볼 일은 없었지만, 언어에 관계없이 적용할 수 있는 최적화의 원칙이나 방법에 대해서도 상세하게 나와있기 때문에 개발자들에게 큰 도움을 주는 책이다.

예제

mac에서 g++을 이용해 test했는데, 대부분 option 없이 g++로 compile하거나 — std=c++14만 사용하면 compile에 별 문제가 없었다.

❯ g++ --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Comments