최신 C ++로 무료 성능을 얻을 수 있습니까? 정당화는 보통 rvalue 생성자가 자동으로 생성되거나 STL의

C ++ 11/14는 단지 C ++ 98 코드를 컴파일 할 때에도 성능을 향상시킬 수 있다고 주장합니다. 정당화는 보통 rvalue 생성자가 자동으로 생성되거나 STL의 일부이므로 이동 시맨틱 라인을 따릅니다. 이제 이러한 사례가 실제로 RVO 또는 유사한 컴파일러 최적화로 이미 처리되었는지 궁금합니다.

내 질문은 수정하지 않고 새로운 언어 기능을 지원하는 컴파일러를 사용하여 더 빠르게 실행되는 C ++ 98 코드의 실제 예를 나에게 줄 수 있는지 여부입니다. 필자는 표준 준수 컴파일러가 복사 제거를 수행 할 필요가 없으며 이동 시맨틱이 속도를 가져올 수있는 이유를 이해하지만 병리학 적 사례를보고 싶습니다.

편집 : 명확하게 말하면, 새로운 컴파일러가 이전 컴파일러보다 빠른지 묻지 않고 컴파일러 플래그에 -std = c ++ 14를 추가하는 코드가 있으면 더 빨리 실행됩니다 (복사본은 피하십시오) 이동 의미론 외에 다른 것을 생각 해낼 수 있습니다.



답변

C ++ 11로 C ++ 03 컴파일러를 다시 컴파일하면 구현 품질과 실질적으로 관련이없는 무한한 성능 향상이 발생할 수있는 5 가지 일반적인 범주를 알고 있습니다. 이것들은 모두 이동 의미의 변형입니다.

std::vector 재 할당

struct bar{
  std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03

마다 foo의 버퍼가 03 C ++로 재 할당은 모든 복사 vectorbar.

C ++ 11에서는 대신 bar::data기본적으로 무료 인 s 를 이동합니다 .

이 경우 std컨테이너 내부의 최적화에 의존합니다 vector. 아래의 모든 경우에 std컨테이너를 사용하는 move것은 컴파일러를 업그레이드 할 때 C ++ 11에서 “자동”으로 효율적인 의미 를 갖는 C ++ 객체이기 때문 입니다. std컨테이너 를 포함하는 컨테이너를 차단하지 않는 개체 도 자동으로 개선 된 move생성자를 상속합니다 .

NRVO 실패

NRVO (이름 반환 값 최적화)가 실패하면 C ++ 03에서는 복사시, C ++ 11에서는 이동시 돌아갑니다. NRVO의 실패는 쉽다 :

std::vector<int> foo(int count){
  std::vector<int> v; // oops
  if (count<=0) return std::vector<int>();
  v.reserve(count);
  for(int i=0;i<count;++i)
    v.push_back(i);
  return v;
}

또는:

std::vector<int> foo(bool which) {
  std::vector<int> a, b;
  // do work, filling a and b, using the other for calculations
  if (which)
    return a;
  else
    return b;
}

우리는 세 가지 값, 즉 반환 값과 함수 내에서 다른 두 값을 가지고 있습니다. Elision을 사용하면 함수 내의 값이 반환 값과 ‘병합’되지만 서로는 병합되지 않습니다. 서로 병합하지 않고 반환 값과 병합 할 수 없습니다.

기본적인 문제는 NRVO 제거가 취약하고 return사이트 근처가 아닌 변경 사항이있는 코드가 갑자기 진단을받지 않고 해당 지점에서 성능이 크게 저하 될 수 있다는 것입니다. 대부분의 NRVO 실패 사례에서 C ++ 11은로 끝나고 moveC ++ 03은 복사본으로 끝난다.

함수 인수 반환

여기에서도 제거가 불가능합니다 :

std::set<int> func(std::set<int> in){
  return in;
}

C ++ 11에서는 이것이 저렴합니다. C ++ 03에서는 복사를 피할 방법이 없습니다. 매개 변수 및 리턴 값의 수명 및 위치는 호출 코드에 의해 관리되므로 함수에 대한 인수는 리턴 값으로 제거 할 수 없습니다.

그러나 C ++ 11은 서로 이동할 수 있습니다. 장난감이 적은 예에서는 무언가가 수행 될 수 있습니다 set.

push_back 또는 insert

마지막으로 컨테이너로의 제거는 발생하지 않지만 C ++ 11은 rvalue 이동 삽입 연산자를 오버로드하여 사본을 저장합니다.

struct whatever {
  std::string data;
  int count;
  whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );

C ++ 03에서 임시 whatever가 생성 된 다음 vector에 복사됩니다 v. std::string각각 동일한 데이터를 갖는 2 개의 버퍼가 할당되고 하나는 버려집니다.

C ++ 11에서는 임시 whatever가 생성됩니다. whatever&& push_back과부하는 move벡터에 그 임시이야 v. 하나의 std::string버퍼가 할당되어 벡터로 이동합니다. 공란 std::string은 버립니다.

할당

아래 @ Jarod42의 답변에서 도난당했습니다.

배정은 배제 할 수 없지만 이사 할 수는 있습니다.

std::set<int> some_function();

std::set<int> some_value;

// code

some_value = some_function();

여기서 제거 some_function할 후보를 리턴하지만 오브젝트를 직접 구성하는 데 사용되지 않으므로 제거 할 수 없습니다. C ++ 03에서 위의 결과는 임시 내용이에 복사됩니다 some_value. C ++ 11에서는 some_value기본적으로 무료 로로 이동 합니다.


위의 모든 효과를 얻으려면 이동 생성자와 할당을 합성하는 컴파일러가 필요합니다.

MSVC 2013은 std컨테이너에 이동 생성자를 구현 하지만 사용자 유형에 이동 생성자를 합성하지는 않습니다.

따라서 std::vectors 및 이와 유사한 것을 포함하는 유형 은 MSVC2013에서 그러한 개선을 얻지 못하지만 MSVC2015에서 유형을 가져 오기 시작합니다.

clang과 gcc는 오랫동안 암시 적 이동 생성자를 구현했습니다. 인텔의 2013 컴파일러는 통과하는 경우 암시 적 생성 생성자를 지원합니다 -Qoption,cpp,--gen_move_operations(MSVC2013과의 상호 호환성을 위해 기본적으로 수행하지는 않음).


답변

당신이 같은 것을 가지고 있다면 :

std::vector<int> foo(); // function declaration.
std::vector<int> v;

// some code

v = foo();

C ++ 03에는 사본이 있지만 C ++ 11에는 이동 할당이 있습니다. 이 경우 무료 최적화가 가능합니다.