article thumbnail image
Published 2021. 1. 4. 01:56

Return Value Optimization 이하 RVO 및, Named RVO(NRVO) 와 Copy-Elision 은 C++98 시절부터 도입됬으며, 런타임 퍼포먼스를 향샹시키는 기능을 한다.

 

// Example.1 RVO
#include <iostream>
using namespace std;
struct Rain {   // Note: All methods have side effects
    Rain() { cout << "c'tor" << endl; }
    ~Rain() { cout << "d'tor" << endl; }

    Rain(const Rain&) { cout << "copy c'tor" << endl; }
    Rain(Rain&&) { cout << "move c'tor" << endl; }

    Rain& operator=(const Rain&) {
        cout << "copy assignment" << endl;
        return *this;
    }

    Rain& operator=(Rain&&) {
        cout << "move assignment" << endl;
        return *this;
    }
};

Rain rvo() {
    return Rain();
}

auto main() ->int {
    Rain shit = rvo();
}

// 출력결과 with RVO
// c'tor
// d'tor

// 출력결과 without RVO
// c'tor
// move c'tor
// d'tor
// move c'tor
// d'tor
// d'tor
  • Return Value Optimization (RVO)
    • RVO는 리턴 값으로 temporary object를 생성하여 반환하지 않는 것을 목표로 한다. 그러나 부작용도 존재한다.
    • 일반적인 컴파일러들은 디폴트로 RVO를 사용하는 것으로 설정되어 있으며, RVO가 없을시, 예제 코드에서 컴파일러는 세 개의 Rain 오브젝트를 생성하게 된다.
      • rvo() 안에 있는 temporary object -> c*tor
      • main() 안에 있는 temporary object ->  첫번째 move c'tor
      • shit 이라고 하는 named object -> 두번째 move c'tor
    • RVO는 C++ ABI 차원에서 탑제되었으므로, 비활성화하는 것이 매우 어렵다.
// Example.2 NRVO
Rain rvo() {
    Rain brain;
    return brain;
}

auto main() ->int {
    rvo();
}

// 출력결과 with NRVO
// c'tor
// d'tor

// 출력결과 without NRVO
// c'tor
// move c'tor
// d'tor
// d'tor
  • Named Return Value Optimization (NRVO)
    • NRVO는 이름을 가진 객체가 반환 되었음에도, 복사되지 않는 것을 의미한다.
    • 일반적으로 RVO가 활성화되어 있는 환경에서는 NRVO가 좀처럼 나타나지 않는다.
// Example.3 Copy Elision
void foo(Rain s) {

}
auto main() ->int {
    foo(Rain());
}

// 출력결과 Copy Elision
// c'tor
// d'tor
  • Copy Elisioin
    • Copy Elision은 RVO처럼 리턴값에 국한되지 않는다. RVO는 Copy Elision의 범주에 속하며, 복사를 피한다는 본질적인 개념은 같다.

 

  • RVO가 일어나지 않는 상황
    • Deciding on Instance at Runtime
      • 컴파일러가 함수내의 어떤 인스턴스를 반환할지 모르는 경우
    • Returning a Parameter / Global
      • 스코프를 벗어난 객체를 반환할 경우
    • Returning by std::move()
      • 이 글에서 설명하기에는 복잡한 부분 (참고)
    • Assignment
      • RVO는 리턴값에서 객체가 생성된 경우에만 발생할수 있으므로, 복사/이동 생성자 대신에 operator=를 사용하는 경우에는 발생하지 않는다.
    • Returning Member

 

// Deciding on Instance at Runtime
Rain RainDrop(bool drop_top) {
    Rain a, b;
    if (drop_top) {
        return a;
    }
    else {
        return b;
    }
}


auto main() ->int {
    Rain rain = RainDrop(true);
}

// 출력결과
// c'tor
// c'tor
// move c'tor
// d'tor
// d'tor
// d'tor

 

// Returning a Parameter / Global
Rain global_rain;

Rain RainDrop(Rain drop) {
    return drop;
}

Rain RainGlobal() {
    return global_rain;
}

auto main() ->int {
    // case 1
    Rain rain = RainDrop(global_rain);

    // case 2
    Rain rain2 = RainGlobal();
}

// case 1
// c'tor
// copy c'tor
// move c'tor
// d'tor
// d'tor
// d'tor

// case 2
// c'tor
// copy c'tor
// d'tor
// d'tor

 

// Returning by std::move()
Rain RainDrop() {
    Rain rain;
    return std::move(rain);
}

auto main() ->int {
    Rain drop = RainDrop();
}

// 출력결과
// c'tor
// move c'tor
// d'tor
// d'tor

 

// Assignment
Rain RainDrop() {
    return Rain();
}

auto main() ->int {
    Rain drop = RainDrop();
    drop = RainDrop();
}

// 출력결과
// c'tor
// c'tor
// move assignment
// d'tor
// d'tor

 

// Returning Member
struct RainWrapper{
    Rain rain;
};

Rain RainDrop() {
    return RainWrapper().rain;
}

auto main() ->int {
    Rain drop = RainDrop();
}

// 출력결과
// c'tor
// move c'tor
// d'tor
// d'tor
복사했습니다!