[Effective C++] 30. 인라인 함수는 미주알고주알 따져서 이해해 두자
인라인 함수는 함수처럼 보이고 함수처럼 동작하는데다가, 매크로보다 훨씬 안전하고 쓰기 좋습니다. 또한 컴파일러 최적화는 함수 호출이 없는 코드가 연속적으로 이어지는 구간에 적용되도록 설계되었기 때문에, 인라인 함수를 사용하면 컴파일러가 함수 본문에 대해 문맥별 최적화를 걸기가 용이해집니다.
실제로 대부분의 컴파일러는 아웃라인 함수 호출에 대해 이런 최적화를 적용하지 않습니다. 하지만 세상에 공짜는 없습니다. 인라인 함수의 아이디어는 함수 호출문을 그 함수의 본문으로 바꿔치기 하는 것이라서, 목적 코드의 크기가 커지게 만듭니다. 또한 인라인 함수로 인해 부풀려진 코드는 성능을 저하시킬 것이고, 명령어 캐시 적중률도 떨어뜨릴 것 입니다.
반대로 본문의 길이가 굉장히 짧은 인라인 함수를 사용하면, 함수 본문에 대해 만들어지는 코드의 크기가 함수 호출문에 대해 만들어지는 코드보다 작아질 수도 있습니다. 바로 이런 경우 목적 코드의 크기도 작아지며 명령어 캐시 적중률도 높아지지요.
암시적 inline 함수 선언
참고로, inline은 컴파일러에 대해 ‘요청’을 하는 것이지, ‘명령’을 하는 것이 아닙니다. 이 요청은 inline을 붙이지 않아도 그냥 눈치껏 되는 경우도 있고 명시적으로 할 수도 있습니다. 먼저 암시적으로 하는 방법은 클래스 정의 안에 함수를 바로 정의해 넣으면 컴파일러는 그 함수를 인라인 함수 후보로 찍습니다.
1
2
3
4
5
6
7
8
9
10
class Person
{
public:
...
int age() const { return theAge; }
...
private:
int theAgel
}
이런 함수는 대개 멤버 함수이지만, 항목 46을 보시면 프렌드 함수도 클래스 내부에서 정의 될 수 있다는 내용이 나오니 참고합시다. 어쨌든 이런 경우에는 암시적으로 인라인 함수로 선언 됩니다.
명시적 inline 함수 선언
인라인 함수를 명시적으로 선언하는 방법은 함수 정의 앞에 inline 키워드를 붙이는 것 입니다. 한 예를 표준 라이브러리의 max 템플릿은 대개 다음과 같이 구현되어 있습니다.
1
2
3
template<typename T>
inline const T& std::max(const T& a, const T& b)
{ return a < b ? b : a; }
max가 템플릿이라는 점 때문에 ‘인라인 함수와 템플릿은 대개 헤더 파일 안에 정의한다’라는 이야기가 생각나기도 합니다. 이 점을 오해해서 함수 템플릿은 반드시 인라인 함수이어야 한다고 결론 내리시는 분들이 있는데요. 맞지도 않을 뿐더러 이렇게 알고 계시면 안됩니다.
인라인 함수는 대체적으로 헤더 파일에 들어가 있어야 하는 게 맞습니다. 왜냐하면 대부분의 빌드 환경에서 인라인을 컴파일 도중에 수행하기 때문입니다. 하지만 템플릿 인스턴스화는 인라인과 완전히 별개로 하등 관련이 없습니다. 만들고 있는 함수 템플릿이 굳이 인라인이될 이유가 없다면 그 템플릿을 인라인 함수로 선언하지 않아도 됩니다.(명시적이든 암시적이든 말입니다)
인라인 함수로 선언 돠어도 컴파일러가 인라인으로 동작시키지 않는 경우
- 복잡한 함수(예를 들어 루프, 재귀가 들어있는 경우)
- 가상 함수 호출(inline은 함수 호출 위치에 호출된 함수를 끼워 넣는 작업을 프로그램 실행전에 한다이고, virtual은 어떤 함수를 호출할지 결정하는 작업을 실행 중에 한다 이므로 서로의 의미가 상충됨.)
- 함수의 주소를 취하는 코드가 있는 경우
- 인라인 함수로 선언된 함수를 함수 포인터를 통해 호출하는 경우
- 생성자 소멸자를 인라인으로 선언하는 경우 (이 경우는 솔직히 말도 안됩니다)
또한 위 경우 말고도 대부분의 디버거는 있지도 않은 인라인 함수에 중단점을 걸 수도 없어서 곤란해 합니다.(디버그 빌드에 대해 인라인을 비활성화 해주는 것만으로 황송해야합니다.)
이러한 사유로 우리는 어떤 것을 인라인으로 선언해야할지, 또는 말아야 할지를 고민하게 됩니다. 결론적으로 아무것도 인라인 하지마세요. 정말 필요한 경우에 한해서만 인라인 함수로 선언하는 것으로 시작하십시오. (Person::age() 같은 함수가 그러하겠지요.)
End Note
- 함수 인라인은 작고, 자주 호출되는 함수에 대해서만 하는 것으로 묶어 둡시다. 이렇게 하면 디버깅 및 라이브러리의 바이너리 업그레이드가 용이해지고, 자칫 생길 수 있는 코드 부풀림 현상이 최소화되며, 프로그램의 속력이 더 빨리질 수 있는 여지가 많아집니다.
- 함수 템플릿이 대개 헤더 파일에 들어간다는 일반적인 부분만 생각해서 이들을 inline으로 선언하면 안됩니다.
Reference
- Effective C++ (Scott Meyers)
Leave a comment