C++ 가상 함수, 런타임 오버헤드 없이 성능 개선하는 방법
가상 함수(Virtual Function)는 런타임 다형성을 제공하지만, 간접 호출(Indirection)과 인라인화(Inlining) 제약으로 성능 저하를 유발함
Devirtualization은 컴파일러가 가상 함수 호출을 직접 호출로 대체하는 최적화 기법이며, `-fwhole-program` 및 `-flto` 플래그를 통해 활성화 가능
CRTP(Curiously Recurring Template Pattern)는 정적 다형성을 구현하여 런타임 오버헤드를 제거하고, 컴파일 타임에 함수 호출을 결정함
C++23의 deducing this는 CRTP와 유사한 정적 디스패치를 제공하며, 코드 가독성을 향상시킴
가상 함수 호출의 런타임 오버헤드
가상 함수(Virtual Function)는 런타임 다형성을 가능하게 하지만, 가상 함수 테이블(vtable)과 vptr(가상 함수 테이블 포인터)로 인해 성능 저하를 야기한다. 특히, vptr은 객체 크기를 증가시키고, vtable을 통한 간접 호출은 인라인화(Inlining)를 방해하여 분기 예측 오류와 캐시 효율성을 감소시킨다. 이러한 오버헤드는 성능에 민감한 코드(Latency-Sensitive Path)에서 더욱 두드러지게 나타나며, 프로파일링을 통해 병목 지점을 파악하고 최적화 전략을 수립해야 한다.
Devirtualization을 통한 컴파일러 최적화
컴파일러는 가능한 경우 가상 함수 호출을 직접 호출로 대체하는 Devirtualization을 수행한다. `-fwhole-program` 플래그는 전체 프로그램에 대한 최적화를 활성화하여, 여러 번역 단위(Translation Unit) 간의 상호 작용을 고려한 Devirtualization을 가능하게 한다. 또한, `-flto` (Link-Time Optimization)는 링킹 시점에 최적화를 수행하여, 크로스-TU(Cross-Translation Unit) 최적화를 가능하게 한다. 이러한 컴파일러 플래그를 통해 런타임 오버헤드를 줄일 수 있다.
CRTP(Curiously Recurring Template Pattern)를 활용한 정적 다형성
Devirtualization이 불가능한 경우, 정적 다형성(Static Polymorphism)을 통해 런타임 오버헤드를 제거할 수 있다. CRTP는 기반 클래스를 파생 클래스 자체로 템플릿화하여, 컴파일 타임에 함수 호출을 결정한다. CRTP를 사용하면 vtable과 vptr이 필요 없어지고, 컴파일러는 모든 함수 호출을 인라인화하여 성능을 극대화한다. 하지만, 각 Base 인스턴스는 별개의 타입이 되므로, 공통 런타임 기반(Common Runtime Base)으로 업캐스팅할 수 없다는 단점이 존재한다.
C++23의 deducing this
C++23의 deducing this는 CRTP와 유사한 정적 디스패치를 제공하며, 코드 가독성을 향상시킨다. deducing this를 사용하면, 템플릿화 없이 멤버 함수에서 `this` 포인터의 타입을 추론할 수 있다. 이는 CRTP의 복잡성을 줄이고, 코드 유지보수성(Code Maintainability)을 높이는 데 기여한다. 결과적으로, deducing this는 정적 다형성을 쉽게 구현할 수 있도록 돕고, 컴파일러 최적화를 통해 런타임 성능을 개선한다.