인라인함수

이번 포스팅에서는 인라인 함수에 대해서 알아보도록 하자.
인라인 함수는 사실상 이전 포스팅에서 다루었던 macro와 거의 같은 것이라고 보면된다. 즉, 매크로함수와 같이 함수가 호출되었을때 그 함수가 inline함수 라면 함수가 수행하는 내용을 그 자리에 붙여넣는 것이다. 매크로의 경우는 함수 뿐만이 아니라 변수값, 변수타입 등, 다방면으로 사용할 수 있지만 inline은 함수에 적용이 된다.
이전 포스팅에서 다루었듯 매크로를 이용해 함수를 구현하는 경우에는 생각해야할 것들이 많이있다. 괄호의 유무같은 것에 의해서 실제 원했던 결과가 나오지 않는 경우가 있었다. 그런데 이번 포스팅에서 다룰 inline함수 의 경우에는 다르다. 똑똑한 컴파일러가 똑똑하게 함수가 호출된 부분을 치환해 준다.
따라서 필자는 간단한 함수를 최적화 시키고자 할때 macro를 사용하기 보다는 inline을 사용하는게 좋다고 생각한다. 그렇다면 inline함수macro와 거의 비슷한 일을 한다는 사실을 알았으니 본론으로 들어가 보자.

인라인 함수와 일반 함수의 차이점

우리가 그러면 함수의 종류를 인라인함수와 일반함수로 나누는 이유는 무엇일까?
먼저 일반함수가 정의되고 호출되었을때 일어나는 일을 생각해보자. 일반 함수의 경우에는 호출 되었을 때 새로운 stack 주소에서 작업이 일어난다. 즉, 프로그램 실행 중 함수가 호출 되면 해당 함수가 저장된 주소로 점프시키고 인자 값을 새로운 stack에 저장하며, 함수의 처리가 종결되면 다시 원래의 자리로 돌아오면서 stack을 빠져나오면서 함수가 종결된다.
만약 구현한 함수가 간단한 비교나 수학연산을 한 경우이고 이 함수가 자주 호출이 된다면 함수 자체의 프로세스는 간단하지만 앞뒤로 점프를 수행하고, 점프할 위치를 기억하는 등의 일을 수행해야한다. 따라서 간단한 함수지만 함수의 호출 과정에 의해 시간이 오래 걸린다. 이러한 단점을 극복하는 방법에는 매크로를 이용하는 방법과 인라인함수를 이용하는것이 있다. 인라인 함수는 컴파일된 함수 코드가 프로그램의 코드 안에 직접 삽입되어진다(매크로와 비슷함). 이 말은 컴파일러가 함수를 호출하는 대신, 그에 대응하는 함수 코드로 대체한다는 것을 의미하며 함수 호출없이 삽입된 함수 코드를 그 자리에서 처리하므로 해당 함수를 수행하기 위해 프로그램이 다른 주소로 점프했다가 되돌아 올 필요가 없어 속도면에서 유리하다

  • 인라인 함수의 특징
    • inline 선언을 해도 컴파일러가 인라인화를 거부할 수 있다.
    • inline 선언을 안 해도 컴파일러가 인라인화를 할 수 있다.
    • 함수가 크거나 재귀호출인 경우 inline화가 안될 수 있다.
    • 함수 코드의 수행 시간이 짧고 빈번하게 호출되는 함수가 아니라면, 인라인 함수로 인한 절대적인 시간 절약은 그다지 크지 않다.

라는 특징이 있다
즉 인라인 함수를 사용하는 것은 아주 간단한 연산의 함수를 위한 것이라고 할 수 있다. 그 이유는 인라인 함수의 길이가 길어지게 된다면 프로그램 자체의 크기가 너무 커지기 때문이다. 따라서 컴파일러가 이러한 것을 고려하여 사용자가 인라인이라고 정의한 함수라도 너무 길다면 인라인화 시키지 않는것이라고 볼 수 있다.

인라인 함수의 사용법

#include <iostream>

template <typename T>
inline T myInline(T a, T b)
{
    return (a+b);
}

int main(int argc, char const *argv[])
{
    int a = 2;
    int b = 4;
    std::string aa = "aoi";
    std::string bb = "sora";
    auto x = myInline<int>(a,b);
    auto y = myInline<std::string>(aa,bb);
    return 0;
}

이 코드를 실행시켜보면 당연히도 원하던 결과가 잘 나오게 된다.


간단히 만들어 본 예제 코드이다.
실제로 내가 만들어 놓은 inline 함수가 제대로 inline 형태로 컴파일이 되었는지 확인하는 방법은 g++ 컴파일러 기준으로 -Winline 으로 warning 메세지를 확인하거나 -S 옵션으로 어셈블리를 확인하는 방법이 있다.

그런데! 인라인화가 안되었다.

실제로 인라인화가 제대로 되어있는지 확인하기 위해서 어셈블리를 확인해봤는데 그냥 일반함수처럼 call 이 되고 있었다. 그 이유를 찾아본 결과 g++ compiler option 에서 -O 옵션 때문이였다. 즉 inline 함수는 compiler 에 dependent 하게 처리가 되기때문에 우리는 inline함수를 구현햇다 하더라도 컴파일러가 최적화하지 않으면 inline 화 되지 않는다. 그리고 inline 선언을 안하더라도 inline화 될 수도있다.

핵심은 컴파일러 dependent 한 최적화였다!! g++ -O3 …

결론

인라인화는 문법자체는 엄청나게 쉽다. 단순히 inline을 함수 정의 앞에 적으면 된다. 다만 이렇게 inline화를 시킨다고 명시했다 치더라도 컴파일러에 따라서 Inline화 시킬것인지 말지는 알아서 정해지는 것이기 때문에 실제로 적용이 되었는지 정말 궁금하다면 assembly를 뜯어서 확인해봐야 할 것이다.

  • 간단한 문법은 인라인화를 적극적으로 활용하자!