728x90
반응형

인라인 함수의 장단점

인라인 함수는 함수 호출문을 그 함수의 본문으로 바꿔치기하는 것이다.

따라서 목적 코드의 크기가 커질 염려가 있고 메모리가 제한된 컴퓨터에서 아무 생각 없이 인라인을 남발했다가는 프로그램 크기가 그 기계에서 쓸 수 있는 공간을 넘어버릴 수도 있다. 가상 메모리를 쓰는 환경일지라도 인라인 함수로 인해 부풀려진 코드는 성능의 걸림돌이 되기 쉽다. 페이징 횟수가 늘어나고, 명령어 캐시 적중률이 떨어질 가능성도 높아진다.

그러나 본문 길이가 굉장히 짧은 인라인 함수를 사용하면, 함수 본문에 대해 만들어지는 코드의 크기가 함수 호출문에 대해 만들어지는 코드보다 작아질 수도 있다. 이렇게 되면 목적 코드의 크기도 작아지고 명령어 캐시 적중률도 높아진다.

 

인라인 함수에 대한 오해

인라인 함수의 inline은 컴파일러에게 '요청'을 하는 것이지 '명령'을 하는 것이 아니다.

따라서 이 요청은 inline을 붙이지 않아도 그냥 눈치껏 되는 경우(암시적)도 있고 명시적으로 할 수도 있다. 또한 인라인 함수와 템플릿은 대개 헤더 파일 안에 정의한다는 것에 따라서 함수 템플릿이 반드시 인라인이어야 한다고 오해할 수 있는데 이는 잘못된 것이다. 물론 몇몇의 컴파일러를 제외하고는 인라인 함수와 템플릿은 컴파일 타임에 실행하기 때문에 헤더 파일 안에 있는 것이 맞다. 그러나 함수 템플릿 중에 인라인화 하고 싶은 것을 인라인 함수로 만드는 것이지 반드시 그럴 필요는 없다.

 

컴파일러가 절대 인라인 함수로 만들지 않는 경우

  • 자신이 보기에 복잡한 함수
    • 루프가 들어있거나 재귀 함수인 경우
  • 가상 함수
  • 함수의 주소를 취하는 코드
    • 이 코드를 위해 본문을 만들어야 하기 때문이다.
  • 함수 포인터를 통해 호출하는 경우
    • 이 코드를 위해 본문을 만들어야 하기 때문이다.

 

생성자와 소멸자는 인라인 하기에 좋지 않다

class Base
{
private:
    std::string bm1, bm2;
};

class Derived :public Base
{
public:
    // 진짜 비어있는 것이 아님
    Derived(){}
private:
    std::string dm1, dm2, dm3;
};

new와 delete를 호출하면 자동적으로 생성자와 소멸자가 호출되며 그것들은 데이터 멤버를 생성하고 소멸시킨다.

따라서 위 코드의 생성자가 비어있다고 해도 실제로 비어있는 것이 아니다. 물론 소멸자도 마찬가지이다.

이것들을 인라인 함수로 정하는 경우 다음과 같은 일들이 발생한다.

// 비어있는 Derived 생성자가 실제로 구현된다면 이런 모습일지도 모른다는 개념적인 코드
Derived::Derived()
{
    Base::Base(); // Base 부분 초기화
    try { dm1.std::string::string(); } // dm1의 생성을 시도
    catch (...)
    {
        Base::~Base(); // 생성 도중에 예외를 던지면 기본 클래스 부분을 소멸 시키고 그 예외를 던짐
        throw;
    }
    
    try { dm2.std::string::string(); } // dm2의 생성을 시도
    catch (...)
    {
        dm1.std::string::~string();  // 생성 도중에 예외를 던지면 dm1, 기본 클래스 부분을 소멸 시키고 그 예외를 던짐
        Base::~Base();
        throw;
    }
    
    try { dm3.std::string::string(); } // dm3의 생성을 시도
    catch (...)
    {
        dm2.std::string::~string();  // 생성 도중에 예외를 던지면 dm1, 기본 클래스 부분을 소멸 시키고 그 예외를 던짐
        dm1.std::string::~string();
        Base::~Base();
        throw;
    }
}

따라서 생성자와 소멸자는 인라인 하기에 좋지 않다는 것이다.

 

inline사용 기본 전략

  • 아무것도 인라인 하지 마라
  • 꼭 인라인 해야 하는 단순한 함수에 한해서만 인라인 함수로 선언한다.
  • 인라인을 주의해서 사용해서, 디버깅하고 싶은 부분에서 디버거를 제대로 쓸 수 있게 하자.
  • 필요한 위치에 인라인 함수를 놓는다.(수동으로 최적화)
  • 80-20 법칙을 잊지 말자. 전체 코드 결과의 80%는 20%의 코드가 원인이다. 20%의 코드를 잘 찾아서 최적화 하자.
  • 함수 자체를 똑바로 만들자.

 

요약

  • 함수 인라인은 작고, 자주 호출되는 함수에 대해서만 하는 것으로 묶어두자. 이렇게 하면 디버깅 및 라이브러리의 바이너리 업그레이드가 용이해지고, 자칫 생길 수 있는 코드 부풀림 현상이 최소화되며, 프로그램의 속력이 더 빨라질 수 있는 여지가 최고로 많아진다.
  • 함수 템플릿이 대개 헤더 파일에 들어간다는 일반적인 부분만 생각해서 이들을 inline으로 선언하면 안 된다.
728x90
반응형