std :: shared_ptr에 상응하는 원자가 아닌 것이 있습니까? 그런데 왜 <memory>에 하나가 없나요? 이것은 원자성에 관한 두 부분으로

이것은 원자성에 관한 두 부분으로 구성된 질문입니다 std::shared_ptr.

1.
내가 말할 수있는 한, 원 자성 std::shared_ptr에서 유일한 스마트 포인터입니다 <memory>. std::shared_ptr사용 가능한 비 원자 버전이 있는지 궁금합니다 (에서 아무것도 볼 수 없으므로 <memory>Boost의 것과 같이 표준 외부의 제안에도 열려 있습니다). 나는 boost::shared_ptr또한 원자 ( BOOST_SP_DISABLE_THREADS정의되지 않은 경우 ) 라는 것을 알고 있지만 다른 대안이있을 수 있습니까? 와 동일한 의미를 std::shared_ptr갖지만 원 자성이없는 것을 찾고 있습니다.

2. 나는 왜 std::shared_ptr원 자성 이 있는지 이해합니다 . 좀 좋네요. 그러나 모든 상황에 적합하지는 않으며 C ++는 역사적으로 “사용한만큼만 지불”하는 만트라를 가지고 있습니다. 여러 스레드를 사용하지 않거나 여러 스레드를 사용하지만 스레드간에 포인터 소유권을 공유하지 않는 경우 원자 스마트 포인터는 과잉입니다. 두 번째 질문은 std::shared_ptrC ++ 11에서 제공되는 비 원자 버전이 아니 었 습니까? ( 이유 가 있다고 가정 ) (답이 단순히 “비 원자 버전은 고려되지 않았습니다”또는 “아무도 비 원자 버전을 요청한 적이 없음”이면 괜찮습니다!).

질문 # 2에서 누군가가 shared_ptr(Boost 또는 표준위원회에) 의 비 원자 버전을 제안했는지 궁금합니다 ( 원자 버전 을 대체하지 않고 shared_ptr공존하기 위해). 구체적인 이유.



답변

1. std :: shared_ptr의 비 원자 버전이 있는지 궁금합니다.

표준에서 제공하지 않습니다. “타사”라이브러리에서 제공하는 것이있을 수 있습니다. 실제로 C ++ 11 이전과 Boost 이전에는 모든 사람이 자신을 포함하여 자신의 참조 카운트 스마트 포인터를 작성한 것처럼 보였습니다.

2. 두 번째 질문은 왜 C ++ 11에서 제공되는 std :: shared_ptr의 비 원자 버전이 아니 었습니까?

이 질문은 2010 년 Rapperswil 회의에서 논의되었습니다.이 주제는 스위스의 National Body Comment # 20에 의해 소개되었습니다. 귀하의 질문에 귀하가 제공 한 것을 포함하여 논쟁의 양측에 강력한 논쟁이있었습니다. 그러나 토론이 끝날 무렵 .NET의 동기화되지 않은 (비 원자) 버전을 추가하는 것에 대해 압도적으로 (만장일치가 아님) 투표가 이루어졌습니다 shared_ptr.

포함에 대한 주장 :

  • 동기화되지 않은 shared_ptr로 작성된 코드는 나중에 스레드 코드에서 사용되어 결과적으로 경고없이 문제를 디버그하기 어렵게 만들 수 있습니다.

  • 올린 사람 : 참조 카운팅 트래픽에 “하나의 방법”하나를 갖는 “보편적”shared_ptr의 혜택이 원래의 제안 :

    사용 된 기능에 관계없이 동일한 객체 유형을 가지며 타사 라이브러리를 포함한 라이브러리 간의 상호 운용성을 크게 향상시킵니다.

  • 원자 학의 비용은 0은 아니지만 압도적이지 않습니다. 원자 적 연산을 사용할 필요가없는 이동 구성 및 이동 할당을 사용하면 비용이 완화됩니다. 이러한 작업은 일반적으로 vector<shared_ptr<T>>지우기 및 삽입에 사용됩니다 .

  • 사람들이 실제로 원하는 일이라면 원자가 아닌 참조 카운트 스마트 포인터를 작성하는 것을 금지하는 것은 없습니다.

그날 Rapperswil에서 LWG의 마지막 단어는 다음과 같습니다.

CH 20을 거부합니다. 현재로서는 변경할 합의가 없습니다.


답변

Howard ‘s는 이미 질문에 잘 대답했으며 Nicol은 호환되지 않는 많은 포인터 유형이 아닌 단일 표준 공유 포인터 유형을 갖는 이점에 대해 몇 가지 좋은 점을 지적했습니다.

위원회의 결정에 전적으로 동의하지만 특수한 경우에 비동기식 shared_ptr유형 을 사용하는 것이 약간의 이점이 있다고 생각 하므로 주제를 몇 번 조사했습니다.

여러 스레드를 사용하지 않거나 여러 스레드를 사용하지만 스레드간에 포인터 소유권을 공유하지 않는 경우 원자 스마트 포인터는 과잉입니다.

프로그램이 여러 스레드를 사용하지 않을 때 GCC를 사용하면 shared_ptr은 refcount에 원자 연산을 사용하지 않습니다. 이것은 프로그램이 다중 스레드인지 (GNU / 리눅스에서는 프로그램이에 연결되는지 여부를 감지하여 간단하게 수행되는) 래퍼 함수를 ​​통해 참조 횟수를 업데이트하고 libpthread.so그에 따라 원자 적 또는 비원 자적 작업에 디스패치함으로써 수행됩니다.

몇 년 전에 GCC shared_ptr<T>__shared_ptr<T, _LockPolicy>기본 클래스 측면에서 구현 되었기 때문에 명시 적으로 사용하여 다중 스레드 코드에서도 단일 스레드 잠금 정책으로 기본 클래스를 사용할 수 있음을 깨달았습니다 __shared_ptr<T, __gnu_cxx::_S_single>. 안타깝게도 이는 의도 된 사용 사례가 아니었기 때문에 GCC 4.9 이전에는 최적으로 작동하지 않았고 일부 작업은 여전히 ​​래퍼 함수를 ​​사용했기 때문에 명시 적으로 _S_single정책을 요청 했음에도 불구하고 원자 작업으로 전달되었습니다 . http://gcc.gnu.org/ml/libstdc++/2007-10/msg00180.html 에서 포인트 (2) 참조자세한 내용과 다중 스레드 앱에서도 비 원자 구현을 사용할 수 있도록 GCC에 대한 패치를 참조하십시오. 나는 몇 년 동안 그 패치에 앉아 있었지만 마침내 GCC 4.9에 적용했습니다. 이렇게하면 이와 같은 별칭 템플릿을 사용하여 스레드로부터 안전하지 않지만 약간 더 빠른 공유 포인터 유형을 정의 할 수 있습니다.

template<typename T>
  using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;

이 유형은 상호 운용이 불가능하며 사용자가 추가로 제공 한 동기화 없이는 스레드간에 개체가 공유되지 않을 std::shared_ptr<T>경우에만 사용하기에 안전 shared_ptr_unsynchronized합니다.

물론 이것은 완전히 이식 할 수 없지만 때로는 괜찮습니다. 올바른 전 처리기 해킹을 사용하면가 shared_ptr_unsynchronized<T>별칭 인 경우 다른 구현에서 코드가 여전히 잘 작동합니다 shared_ptr<T>. GCC에서는 조금 더 빠릅니다.



4.9 이전에 GCC를 사용하는 경우 _Sp_counted_base<_S_single>명시 적 전문화를 자신의 코드 에 추가하여이를 사용할 수 있습니다 (그리고 __shared_ptr<T, _S_single>ODR 위반을 방지하기 위해 전문화를 포함하지 않고 인스턴스화 하지 않도록 보장 ). 이러한 std유형의 특수화를 추가하는 것은 기술적으로 정의되지 않았지만 이 경우 GCC에 전문화를 추가하는 것과 자신의 코드에 추가하는 것 사이에는 차이가 없기 때문입니다.


답변

두 번째 질문은 왜 C ++ 11에서 제공되는 std :: shared_ptr의 비 원자 버전이 아니 었습니까? (이유가 있다고 가정).

침입 포인터가없는 이유나 공유 포인터의 다른 가능한 변형이없는 이유를 쉽게 물어볼 수 있습니다.

shared_ptrBoost에서 물려받은 의 디자인은 스마트 포인터의 최소 표준 lingua-franca를 만드는 것입니다. 일반적으로 말해서, 이것을 벽에서 떼어 내서 사용할 수 있습니다. 다양한 응용 분야에서 일반적으로 사용되는 것입니다. 인터페이스에 넣을 수 있으며 좋은 사람들이 기꺼이 사용할 가능성이 있습니다.

스레딩은 앞으로 많이 보급 될 것입니다. 실제로 시간이 지남에 따라 스레딩은 일반적으로 성능을 달성하는 주요 수단 중 하나입니다. 스레딩을 지원하는 데 필요한 최소한의 작업을 수행하기 위해 기본 스마트 포인터가 필요하면 이러한 현실이 가능해집니다.

사소한 차이가있는 6 개의 스마트 포인터를 표준에 덤핑하거나 정책 기반 스마트 포인터보다 더 나쁜 것은 끔찍했을 것입니다. 모든 사람은 자신이 가장 좋아하는 포인터를 선택하고 다른 모든 것을 맹세합니다. 아무도 다른 사람과 의사 소통 할 수 없습니다. 모든 사람이 자신의 유형을 가지고있는 C ++ 문자열의 현재 상황과 같습니다. 문자열과의 상호 운용이 스마트 포인터 클래스 간의 상호 운용보다 훨씬 쉽기 때문입니다.

Boost, 그리고 더 나아가위원회는 사용할 특정 스마트 포인터를 선택했습니다. 기능의 균형이 잘 맞았으며 실제로 널리 사용되고 일반적으로 사용되었습니다.

std::vector일부 코너 케이스에서도 네이 키드 어레이에 비해 비 효율성이 있습니다. 몇 가지 제한 사항이 있습니다. 일부 용도 vector는 던지는 할당자를 사용하지 않고. 그러나위원회는 vector모든 사람을위한 모든 것이되도록 설계하지 않았습니다 . 대부분의 응용 프로그램에서 좋은 기본값으로 설계되었습니다. 그것이 작동하지 않는 사람들은 그들의 필요에 맞는 대안을 작성할 수 있습니다.

shared_ptr원 자성이 부담 이라면 스마트 포인터를 위해 할 수있는 것처럼 . 그런 다음 다시 한 번, 너무 많이 복사하지 않는 것을 고려할 수도 있습니다.


답변

직장에서 shared_ptr에 대한 강연을 준비 중입니다. 나는 (make_shared가 할 수있는 것과 같은) 별도의 malloc을 피하고 위에서 언급 한 shared_ptr_unsynchronized와 같은 잠금 정책에 대한 템플릿 매개 변수를 사용하여 수정 된 boost shared_ptr을 사용하고 있습니다. 나는 프로그램을 사용하고 있습니다

http://flyingfrogblog.blogspot.hk/2011/01/boosts-sharedptr-up-to-10-slower-than.html

테스트로 불필요한 shared_ptr 복사본을 정리 한 후. 프로그램은 주 스레드 만 사용하며 테스트 인수가 표시됩니다. 테스트 환경은 linuxmint 14를 실행하는 노트북입니다. 다음은 초 단위의 시간입니다.

테스트 실행 설정 boost (1.49) std with make_shared 수정 된 boost
mt-unsafe (11) 11.9 9 / 11.5 (-pthread on) 8.4
원자 (11) 13.6 12.4 13.0
mt-unsafe (12) 113.5 85.8 / 108.9 (-pthread on) 81.5
원자 (12) 126.0 109.1 123.6

‘std’버전 만 -std = cxx11을 사용하고 -pthread는 g ++ __shared_ptr 클래스에서 lock_policy를 전환 할 가능성이 높습니다.

이 수치를 통해 원자 적 지시가 코드 최적화에 미치는 영향을 알 수 있습니다. 테스트 케이스는 C ++ 컨테이너를 사용 vector<shared_ptr<some_small_POD>>하지 않지만 객체에 스레드 보호가 필요하지 않으면 문제가 발생할 수 있습니다. 추가 malloc이 인라인 및 코드 최적화의 양을 제한하기 때문에 Boost는 덜 영향을받습니다.

원자 명령의 확장 성을 스트레스 테스트하기에 충분한 코어가있는 머신을 아직 찾지 못했지만, 필요할 때만 std :: shared_ptr을 사용하는 것이 아마도 더 좋습니다.


답변

Boost는 shared_ptr비 원자적인 것을 제공합니다 . 이라고 local_shared_ptr하며 boost의 스마트 포인터 라이브러리에서 찾을 수 있습니다.