본문 바로가기
C++

[성능] shared_ptr의 thread safety에 대해

by 매운돌 2023. 2. 16.

최근 CppCon 유튜브 채널에서 Efficient Concurrent Memory Management라는 주제의 영상을 보게되었습니다. 내용은 멀티 스레드 환경에서 메모리를 안전(RAII)하게 사용하면서, 성능도 놓치지 않는 방법을 제시하는 영상이었습니다. 그리고 이러한 방법중 하나로 atomic shared_ptr을 이야기를 하고 있습니다. 그런데 개인적으로 shared_ptr를 Muti Thread 환경에서 사용한 경험이 많이 없어서, 이 영상을 보면서 꽤 흥미로운 부분들을 많이 알 수 있게 되었습니다. 따라서 오늘은 이 영상의 초반 부분에 해당하는 shared_ptr의 Multi Thread 상황에 대해서 이야기 해보겠습니다.


Thread1이 잠시 sleep된 동안 Thread2가 Thread1이 접근하고 있는 데이터를 삭제한 상황

위 이미지는 Muti Thread 상황에서 얼마든지 발생할 수 있습니다. 그리고 이러한 상황은 shared_ptr를 통해서 해결이 가능할거 같습니다. 왜냐하면 shared_ptr는 referece count를 통해 RAII방식으로 Object를 관리하기 때문에 만약 위의 List가 shared_ptr로 관리되고 있었다고 하면, Thread1이 해당 객체를 참조하고 있는한, 객체가 삭제되어 발생하는 이슈들은 방지할 수 있습니다. 그런데 이 조건이 성립되기 위해서는 shared_ptr가 thread safety하다는 조건이 있어야 합니다. 

 

cppreference에서 shared_ptr의 thread safety에 대한 내용

cppreference에서 shared_ptr에 대해 이렇게 설명하고 있습니다. "shared_ptr의 제어 블록은 Thread로부터 안전하다."  이 뜻은 reference count를 update하는 부분은 atomic하게 동작하고 있고, 따라서 reference count가 0이 되어 객체를 삭제하는 작업도 정확히 한 번만 수행한다고 보장할 수 있습니다.

Thread4에서 s3를 s4에 대입할 때, s3는 other_thing을 참조하거나 s2를 참조할 수 있습니다.

하지만, 위의 이미지 처럼 각각의 Thread에서 다른 Thread의 shared_ptr을 참조할 때, 이 객체가 s2 객체인지 아니면 other_thing 객체인지 단정지을 수 없습니다. 따라서 shared_ptr는 궁극적으로 thread safety하지 않다고 말할 수 있고, Muti Thread 상황에서 올바른 동작을 기대하려면  shared_ptr overloads of atomic functions을 사용해야 합니다.

atomic shared pointer을 사용하면, thread safety한 stack을 단 15줄로 만들 수 있습니다.

그리고 atomic shared_ptr를 사용하면 정말 간단하게 thread safety한 자료 구조를 완성할 수 있습니다. 하지만 이와 반비례로 성능에서는 별로 좋지 못한 결과를 보여줍니다.

아래의 그래프는 BST를 atomic shared_ptr를 통해서 구현 했을때 와 Baseline(Delete가 없는 상황, 즉 Memory 관리가 전혀 필요없는 경우)를 비교한 것 입니다.

이 영상에서 발표자는 위 그래프를 그리기 위해서 사용한 atomic shared_ptr BTS는 자신이 알고 있는 가장 빠른 버전이라고 이야기 하고 있습니다. 하지만 Baseline에 비해 처리량이 현격히 떨어집니다.

Root로 부터 w를 찾기 위한 과정

위 이미지를 보면 이전의 그래프 결과를 바로 납득할 수 있습니다. 단지 w값을 읽기 위한 과정일 뿐이지만, reference count가 갱신되고 있음을 알 수 있습니다. 즉 reference count는 atomic하게 동작하여 값을 갱신할 것이고, 이 말은 각각의 Thread들이 Serialize되어 읽기만으로 엄청난 성능 하락을 보이게 됩니다.

 

Reference


std::shared_ptr - cppreference.com

 

std::shared_ptr - cppreference.com

template< class T > class shared_ptr; (since C++11) std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when

en.cppreference.com

Smarter Cpp Atomic Smart Pointers - Efficient Concurrent Memory Management - Daniel Anderson CppCon - YouTube