std::shared_ptrが内部で確保するメモリについての調査&スレッド対応について

C++11で追加されたstd::shared_ptrやstd::unique_ptrなどのポインタを管理するテンプレートクラスは本当に便利です。ですが、内部で確保されるメモリや処理について考えておかないと思わぬところで処理速度の低下やメモリ不足に陥ります。特にゲームプログラムのメモリ管理は厳しくなる傾向があるため、shared_ptrが内部で確保するメモリのサイズや回数について調べてみました。ちなみにunique_ptrはほぼデメリットはありません。生ポインタと同じサイズ、includeとテンプレート処理でコンパイルがほんの少し遅くなる程度です。
※VisualStudio2017 x86プラットフォームでの調査 他の環境では結果が異なる場合があります


std::shared_ptrの内部処理

shared_ptrは参照カウンタを利用したスマートポインタなので、カウンターを保持、管理するクラスが内部で作成されます。shared_ptrを作成する毎に数十byteの追加のメモリ確保を行います。また、ポインターをコピー(operator=)する毎に参照カウンタの増減が発生します。shared_ptrでの主な追加コストはこの2つになります。


std::shared_ptr内部で確保されるメモリサイズと回数

shared_ptrの一番のコストはやはりメモリ確保になります。shared_ptrのコンストラクターの引数を変えることで、独自のアロケータを使用したりメモリ確保の回数を減らしたりできるようになっています。それぞれについて検証するプログラムを作成しました。ソースコードと検証結果は以下のようになります。


std::shared_ptrメモリ操作対応表

Windows(x86) shared_ptr内部で行われるメモリ操作。メモリサイズはあくまで目安です。クラスのアラインメントや環境によって数値が変わる場合があります。

shared_ptrで管理するクラスT カウンター管理クラス
コンストラクター メモリ確保 メモリ解放 サイズ メモリ確保/解放 サイズ
shared_ptr<T>(T* p)  new delete sizeof(T) new/delete 16byte
shared_ptr<T>(T* p, Deleter) 任意 Deleter(p); sizeof(T) new/delete 16byte
shared_ptr<T>(T* p, Deleter, MyAllocator<T>()) 任意 Deleter(p); sizeof(T) MyAllocator 16byte
make_shared<T>(…); なし※ ~T() 0 new/delete sizeof(T) +12byte
allocate_shared<T>(MyAllocator<T>(),…) なし※ ~T() 0 MyAllocator sizeof(T) +12byte

※make_sharedとallocate_sharedはカウンター管理クラスとまとめて1回のメモリ確保


make_sharedとallocate_sharedの注意点

1回のメモリ確保で済むため効率的ですが、weak_ptrで弱参照している場合、参照カウンタ0になりデストラクタが実行されてもメモリは解放されません。参照カウンタとまとめてメモリ確保しているためweak_ptrからの弱参照がなくなるまで無駄なメモリが残り続けます。サイズの大きいクラスをmake_sharedやallocate_sharedで作成しweak_ptrから参照するとメモリ効率が悪化する可能性があります。


shared_ptrでのスレッド対応

shared_ptrでスレッドセーフな処理は、参照カウンタの操作のみです。カウンタの整数値をアトミック操作しているだけで、他の操作はスレッドセーフではありません。->演算子でのメンバ変数や関数アクセスがスレッドセーフになるわけではありません。自分で実装する必要があります。それと、マルチスレッドでshared_ptrを使う際はデストラクタがいつ、どこで、だれから呼ばれるか予測が難しいため、デストラクタから呼ばれる関数がスレッドセーフかどうか十分確認することをお勧めします。プログラムの構造や処理時間が変わることでshared_ptrの参照カウンタが0になる場所、タイミングが変わり、突然原因不明のバグに襲われるかもしれません。


shared_ptr用メモリアロケーター例

VisualStuio2017の<xmemory0>のallocatorクラス(STLのデフォルトアロケータ)のメモリ確保関数を置き換えたものです。::operator newをWindows専用の_aligned_mallocに変更。

広告

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.