Programing

왜 std :: move std :: shared_ptr을 사용합니까?

lottogame 2020. 7. 3. 18:39
반응형

왜 std :: move std :: shared_ptr을 사용합니까?


Clang 소스 코드를 살펴본 결과이 스 니펫을 발견했습니다.

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = std::move(Value);
}

왜 내가 원하는 것 ?std::movestd::shared_ptr

공유 리소스에 대한 소유권을 이전 할 수 있습니까?

왜 대신 대신이 작업을 수행하지 않습니까?

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = Value;
}

다른 답변이 충분히 강조하지 않은 것은 속도 의 포인트라고 생각합니다 .

std::shared_ptr참조 카운트는 atomic 입니다. 기준 카운트를 늘리거나 줄이려면 원자 적 증가 또는 감소 가 필요합니다 . 이것은 원자가 아닌 증가 / 감소 보다 백 배 느리지, 동일한 카운터를 증가시키고 감소 시키면 정확한 수로 감겨 프로세스에 많은 시간과 자원을 낭비한다는 것은 말할 것도 없습니다.

shared_ptr복사 하는 대신 이동하여 원자 참조 카운트를 "훔치고" 다른 것을 무효화합니다 shared_ptr. "스틸 링"참조 횟수는 원자 적이 지 않으며 복사하는 것 shared_ptr(및 원자 참조 증분 또는 감소를 야기) 보다 백 배 빠릅니다 .

이 기술은 최적화를 위해 순수하게 사용됩니다. 복사 (제안한대로)는 기능면에서도 훌륭합니다.


사용 move하면 주식 수를 늘리고 바로 줄이지 않습니다. 그러면 사용 횟수에 대한 고가의 원자 작업이 절약 될 수 있습니다.


이동 (이동 생성자 등) 작업이 std::shared_ptr있는 저렴한 가 기본적이기 때문에, "도난 포인터" (소스로부터 목적지에 더욱 정확하게는 온 상태 제어 블록은 레퍼런스 카운트 정보를 포함하는, 소스로부터 목적지로 "도난"된다) .

대신 원자 참조 카운트 증가 (예 : 정수 데이터 멤버뿐만 아니라 Windows 호출)의 복사 작업 포인터 / 상태를 훔치는 것 보다 비용 이 많이 듭니다 .std::shared_ptr++RefCountRefCountInterlockedIncrement

따라서이 경우의 참조 횟수 역학을 자세히 분석하십시오.

// shared_ptr<CompilerInvocation> sp;
compilerInstance.setInvocation(sp);

값을 전달한 sp다음 메소드 내에서 사본 을 가져 오면 다음을 수행 CompilerInstance::setInvocation할 수 있습니다.

  1. 메소드를 입력 할 때 shared_ptr매개 변수는 복사 구성됩니다. ref count atomic incremental .
  2. 메소드 본문 내에서 매개 변수를 데이터 멤버에 복사 합니다 shared_ptr. ref count atomic incremental .
  3. 메소드를 종료하면 shared_ptr매개 변수가 삭제됩니다 (ref count atomic decrement) .

3 개의 원자 연산에 대해 2 개의 원자 증분과 1 개의 원자 감소가 있습니다.

대신 Clang의 코드에서 올바르게 수행 된 것처럼 shared_ptr값으로 매개 변수 를 전달한 다음 std::move메서드 내부에 매개 변수 를 전달하면 다음과 같은 이점이 있습니다.

  1. 메소드를 입력 할 때 shared_ptr매개 변수는 복사 구성됩니다. ref count atomic incremental .
  2. 메소드 본문 내에서 데이터 멤버에 std::move대한 shared_ptr매개 변수 : 참조 횟수는 변경 되지 않습니다 ! 당신은 포인터 / 상태를 훔치고 있습니다 : 비싼 원자 참조 횟수 작업이 필요하지 않습니다.
  3. 메소드를 종료하면 shared_ptr매개 변수가 삭제됩니다. 그러나 2 단계에서 이동 했으므로 shared_ptr매개 변수가 더 이상 아무것도 가리 키지 않으므로 폐기 할 것이 없습니다. 이 경우에도 원자 감소는 발생하지 않습니다.

결론 :이 경우 하나의 심판 카운트 원자 증분, 즉 하나의 원자 연산 을 얻습니다 .
보시다시피, 이것은 복사 사례에 대해 두 개의 원자 증분과 하나의 원자 감소 (총 세 개의 원자 연산에 대해) 보다 훨씬 습니다 .


를 복사하면 shared_ptr내부 상태 객체 포인터를 복사하고 참조 횟수를 변경합니다. 그것을 이동하는 것은 포인터를 내부 참조 카운터와 소유 한 객체로 바꾸는 것만 포함하므로 더 빠릅니다.


이 상황에서 std :: move를 사용하는 데는 두 가지 이유가 있습니다. 대부분의 응답은 속도 문제를 해결했지만 코드의 의도를보다 명확하게 보여주는 중요한 문제는 무시했습니다.

std :: shared_ptr의 경우 std :: move는 포인트의 소유권 이전을 분명하게 나타내며 간단한 복사 작업은 추가 소유자를 추가합니다. 물론, 원래 소유자가 그 후에 소유권을 포기하면 (예 : std :: shared_ptr을 파기 할 수있게하여) 소유권 이전이 완료된 것입니다.

std :: move로 소유권을 이전하면 무슨 일이 일어나고 있는지 분명합니다. 일반 사본을 사용하는 경우 원래 소유자가 즉시 소유권을 양도한다는 것을 확인할 때까지 의도 한 작업이 전송인지 확실하지 않습니다. 보너스로, 소유권의 원자 이전은 소유자 수가 1 증가한 임시 상태를 피할 수 있기 때문에보다 효율적인 구현이 가능합니다 (및 참조 수가 변경됨).

참고 URL : https://stackoverflow.com/questions/41871115/why-would-i-stdmove-an-stdshared-ptr

반응형