일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- moreeffectiveC++
- 선택정렬
- 데이터애셋
- 람다
- UELOG
- 강참조
- UML관련
- map
- 람다사용정렬
- unorder_map
- 언리얼엔진구조체
- 자료구조
- 정렬
- 크리티컬섹션
- C++최적화
- 알고리즘
- 약참조
- C++
- UE_LOG
- 애셋로드
- enumasByue
- 프로그래머스
- dataasset
- BFS
- 델리게이트
- UE4 커스텀로그
- stl
- 스마트포인터
- 정렬알고리즘
- 언리얼가비지컬렉터
- Today
- Total
기억을 위한 기록들
[CPP]반환 값 최적화(return value optimization)RVO란?/NRVO 본문
이펙티브 c++를 작성하다가 등장하여 관련하여 찾아보았다.
* 틀린 부분이 있을 수도 있습니다.
예제 클래스로 살펴보면 int num 멤버변수를 갖고 있는 클래스 T가 있다.
class T {
public:
/*생성자*/
T(int a, string n)
:num(a)
, name(n)
{
cout <<name<< "객체 생성자 호출 " << endl;
}
/*복사 생성자*/
T(const T& ref)
: num(ref.num)
, name("empty")
{
cout << ref.name << "객체를 복사한, ";
cout << name << " 새로운 객체 복사생성자 호출 " << endl;
}
/*소멸자*/
~T()
{
cout <<name << "객체 : 소멸자 호출" << endl;
}
int num;
string name;
};
별 다른 기능이 없긴 하다.
여기서 전역 함수로 해당 클래스를 복사해 값으로 반환해주는 함수가 있다.
const T copyT(const T& newT)
{
T b(newT.num,"b"); //파라미터로 받은 T객체의 이름을 b라고 넣음.
return b;
}
한 객체를 복사하기 위해 위 함수를 실행해보면
int main()
{
T a(5,"a");
copyT(a);
return 0;
}
실행 결과로 2번의 클래스 생성과 1번의 복사와 3번의 파괴가 일어난다.
순서대로 확인해보면
1. a객체 생성자 호출 : main 함수 a객체 생성한다
2. b객체 생성자 호출 : copyT 함수 안에서 b라는 이름을 가진 T 객체 생성한다.
3. b이름 객체를 복사한, empty 이름을 가지는 복사생성자 호출 :
return 되는 b객체를 복사한 empty이름 객체 복사생성자 호출
4. b객체 소멸자 호출 : copyT함수가 끝나며 스택 메모리를 벗어나며 소멸이 바로 진행되는 b이름 객체 소멸
5. empty객체 소멸자 호출 : main 함수로 돌아와 main함수 종료되며 emtpy이름 객체 소멸
6. a객체 소멸자 호출 : main 함수로 돌아와 main함수 종료되며 a이름 객체 소멸
난 단지 a객체의 정수값을 가지는 객체를 하나 더 만들어서 return 받으려고 한 것뿐인데,
불필요한 생성과 소멸이 더 일어난다. 만약에 T 클래스 자체의 크기가 커지면 더욱 더 비효율적이게 된다.
여기서 이제 RVO(return value optimization)이 적용되는 새로운 객체를 반환되게 하는 복사 함수로 만들어준다면, 출력이 달라진다.
main 함수에서 copyT_rvo 함수를 호출해주면
int main()
{
T a(5,"a");
copyT_rvo(a);
return 0;
}
copyT_rvo함수에서는 객체 생성 후 리턴 해주는것이 아니라 new라는 이름을 가진 객체를 바로 리턴해준다.
const T copyT_rvo(const T& newT)
{
return T(newT.num, "new");
}
이전과 다르게, 복사생성자가 호출되지 않고 단 2개의 객체의 생성과 소멸만 일어난다.
얼핏보기엔 짧은 코드의 차이지만, 내부적으로는 많은 차이가 일어나는 것을 확인 할 수 있다.
그런데 재밌는 부분은 해당 코드는 debug모드에서 실행시켰을때의 예시이고, Release모드로 실행시켜보면 다르게 작동된다.
const T copyT(const T& newT)
{
T b(newT.num,"b"); //파라미터로 받은 T객체의 이름을 b라고 넣음.
return b;
}
이전의 copyT함수를 실행시키면 결과는 불필요한 복사생성자를 호출 하지 않는다.
차이는 바로 Release모드에서는 컴파일러가 이를 감지하고, 최적화를 해주기 때문이다. copyT 함수에서 생성된 T클래스 객체가 return 을 바로 해주는 것을 인지하고 최적화 해주게 되는 것이다.
RVO는 컴파일러 상관없이 해주기에 어떤 모드에서든지 최적화를 해준다.
그리고 NRVO라고 불리는 것도 있는데 Named Return Value Optimization이라고 부르는것이 있는데 이게 바로
const T copyT(const T& newT)
{
T b(newT.num,"b"); //파라미터로 받은 T객체의 이름을 b라고 넣음.
return b;
}
copyT 함수처럼 객체에 이름이 붙여진 반환의 차이라고 생각하면 될것같다.
정리하자면 RVO는
const T copyT_rvo(const T& newT)
{
return T(newT.num, "new");
}
이 자체로 임시 객체 생성없이 생성하는 객체는 바로 반환해주는 것이고, NRVO는 이름 붙여진 객체 반환 즉, 이전에 비교한 copyT함수가 예다.
const T copyT(const T& newT)
{
T b(newT.num,"b"); //파라미터로 받은 T객체의 이름을 b라고 넣음.
return b;
}
비교를 위해 보여준 함수로, NRVO는 Debug모드에서는 최적화가 안되었지만, Relase모드에서는 최적화가 된것을 확인했다. 이는 차이로 RVO는 Relase모드 Debug 모드 상관없이 반환 최적화를 해주는 반면,
NRVO는 최적화 옵션 /O1(크기 최소화)부터 동작을 하게 되서 Debug모드에서는 작동되지 않는다.
'C & CPP' 카테고리의 다른 글
[CPP] C++ 최적화에 관하여 (1-3) (0) | 2021.07.25 |
---|---|
[C&CPP] assert/static_assert 관련 (0) | 2021.04.15 |
[C++] 정수 자료형의 범위/int8, int16, int32, int64.. (0) | 2021.04.02 |
lvalue와 rvalue /rvalue 참조와 이동생성자,이동대입연산자 사용 (0) | 2021.03.28 |
[CPP] 가상(virtual)함수의 동작원리 (0) | 2021.03.10 |