[C++] Virtual function performance
[ C++ : Virtual function performance ]
이는 비가상 함수 호출과 거의 같은 효율성입니다. 대부분의 머신에서 몇 개의 명령어만 더 실행합니다. 따라서 가상 함수를 호출하는 비용은 기본적으로 함수 포인터를 통해 함수를 호출하는 비용과 같습니다. 가상 함수 자체는 일반적으로 성능 병목 현상이 아닙니다.
가상 함수의 실제 런타임 비용은 인라인과의 상호 작용과 관련이 있습니다.
중략
사실상 인라인을 포기하는 것입니다.
위 게시글에서 언급했듯이, 책의 저자(스콧 마이어스)는 가상 함수 그 자체로는 큰 오버헤드가 될 수 없다고 말한다. 실제 성능에 차이나는 요소는 가상함수의 인라인되지 않는 특성이라고 하는데, 궁금해서 추가적으로 스택오버플로우에서 virtual function performance에 관한 글들을 찾아봤으나, 숙련된 C++ 개발자들 사이에서도 의견이 꽤 갈리는 것 같다. C++ 프로젝트의 특성마다 다를 수 있으며, 게임 프로그래머들은 virtual function의 성능 저하에 관한 요인으로 cache miss를 자주 언급하는데, 이와 관련한 참고 자료는 다음과 같다.
[1] Epicgames Network Engineer, Alex Koumandarakis의 Iris Q&A에 의하면,
Iris를 도입하여 달성하기 위한 목표로 기존의 Replication system의 문제점를 언급하며 다음과 같이 말했다.
Unlock scalability by removing antipatterns that constrain it, such as continuously polling game code and using cache-inefficient virtual function calls during serialization.
게임 코드를 지속적으로 polling하고 직렬화 중에 cache-inefficient virtual function 호출을 사용하는 것과 같이 확장성을 제한하는 안티패턴을 제거하여 확장성을 언락(unlock)합니다.
[2] Mark James에 의하면,
성능이 매우 중요한 애플리케이션(예: 비디오 게임)에서 가상 함수 호출은 너무 느릴 수 있습니다. 최신 하드웨어에서 가장 큰 성능 문제는 Cache Miss입니다. 데이터가 캐시에 없으면 사용할 수 있기까지 수백개의 사이클이 걸릴 수 있습니다. 일반 함수 호출은 CPU가 새 함수의 첫 번째 명령어를 fetch하고 캐시에 없을 때 명령어 cache miss를 생성할 수 있습니다. 가상 함수 호출은 먼저 객체에서 vtable 포인터를 로드해야 합니다. 이로 인해 데이터 cache miss가 발생할 수 있습니다. 그런 다음 vtable에서 함수 포인터를 로드하여 또 다른 data cache miss가 발생할 수 있습니다. 그런 다음 함수를 호출하여 비가상 함수와 마찬가지로 instruction cache miss가 발생할 수 있습니다. 많은 경우 2번의 추가 cache miss는 문제가 되지 않지만 성능이 중요한 코드에서 긴밀한 루프에서는 성능이 크게 저하될 수 있습니다.
→ loop내에서 가상 함수를 계속 호출하는 패턴의 경우.
결과적으로, virtual fuction의 cache miss와 inline되지 않는 특성이 잠재적으로 성능에 영향을 미치는 요소가 될 수 있겠다.