code

std :: shared_ptr에 상응하는 원자가 아닌 것이 있습니까?

codestyles 2020. 10. 7. 07:40
반응형

std :: shared_ptr에 상응하는 원자가 아닌 것이 있습니까? 그리고 왜 거기에 하나가 없습니까??


이것은 원자성에 관한 두 부분으로 구성된 질문입니다 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 Framework의 비동기 (비 원자) 버전을 추가하는 것에 대해 압도적으로 (만장일치가 아님) 투표가 이루어졌습니다 shared_ptr.

포함에 대한 주장 :

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

  • 참조 계산에서 트래픽에 대한 "단방향"인 하나의 "범용"shared_ptr을 사용하면 다음과 같은 이점이 있습니다. 원래 제안에서 :

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

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

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

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

CH 20을 거부합니다. 현재 변경에 대한 합의가 없습니다.


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

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

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

프로그램이 여러 스레드를 사용하지 않을 때 GCC를 사용하면 shared_ptr은 refcount에 원자 연산을 사용하지 않습니다. 이것은 프로그램이 다중 스레드인지 (GNU / Linux에서는 프로그램이에 연결되는지 여부를 감지하여 간단히 수행됨) 래퍼 함수를 ​​통해 참조 횟수를 업데이트하고 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의 원자 버전이 아니 었습니까? (이유가 있다고 가정).

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

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

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

Dumping a half-dozen smart pointers with minor variations between them into the standard, or even worse a policy-based smart pointer, would have been terrible. Everyone would pick the pointer they like best and forswear all others. Nobody would be able to communicate with anyone else. It'd be like the current situations with C++ strings, where everyone has their own type. Only far worse, because interoperation with strings is a lot easier than interoperation between smart pointer classes.

Boost, and by extension the committee, picked a specific smart pointer to use. It provided a good balance of features and was widely and commonly used in practice.

std::vector has some inefficiencies compared to naked arrays in some corner cases too. It has some limitations; some uses really want to have a hard limit on the size of a vector, without using a throwing allocator. However, the committee didn't design vector to be everything for everyone. It was designed to be a good default for most applications. Those for whom it can't work can just write an alternative that suites their needs.

Just as you can for a smart pointer if shared_ptr's atomicity is a burden. Then again, one might also consider not copying them around so much.


I am preparing a talk on shared_ptr at work. I have been using a modified boost shared_ptr with avoid separate malloc (like what make_shared can do) and a template param for lock policy like shared_ptr_unsynchronized mentioned above. I am using the program from

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

as a test, after cleaning up the unnecessary shared_ptr copies. The program uses the main thread only and the test argument is shown. The test env is a notebook running linuxmint 14. Here is the time taken in seconds:

test run setup    boost(1.49)      std with make_shared     modified boost
mt-unsafe(11)         11.9         9/11.5(-pthread on)          8.4  
atomic(11)            13.6            12.4                     13.0  
mt-unsafe(12)        113.5         85.8/108.9(-pthread on)     81.5  
atomic(12)           126.0           109.1                    123.6  

Only the 'std' version uses -std=cxx11, and the -pthread likely switches lock_policy in g++ __shared_ptr class.

From these numbers, I see the impact of atomic instructions on code optimization. The test case does not use any C++ containers, but vector<shared_ptr<some_small_POD>> is likely to suffer if the object doesn't need the thread protection. Boost suffers less probably because the additional malloc is limiting the amount of inlining and code optimizaton.

I have yet to find a machine with enough cores to stress test the scalability of atomic instructions, but using std::shared_ptr only when necessary is probably better.


Boost provides a shared_ptr that's non-atomic. It's called local_shared_ptr, and can be found in the smart pointers library of boost.

참고URL : https://stackoverflow.com/questions/15129263/is-there-a-non-atomic-equivalent-of-stdshared-ptr-and-why-isnt-there-one-in

반응형