web-dev-qa-db-ja.com

std :: shared_ptrの非アトミックな同等物はありますか?そして、なぜ<memory>にないのですか?

これはstd::shared_ptrの原子性に関するすべての2つの部分からなる質問のビットです。

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++には歴史的に「使用した分だけ支払う」というスローガンがありました。複数のスレッドを使用していない場合、または複数のスレッドを使用しているが、スレッド間でポインターの所有権を共有していない場合、アトミックスマートポインターは過剰です。私の2番目の質問はなぜC++ 11で提供されているstd::shared_ptrの非アトミックバージョンではなかったのですか? (whyが存在すると仮定)(答えが単に「非原子バージョンは単に考慮されなかった」または「誰も要求したことがない」場合非原子バージョン」で結構です!)。

質問#2で、誰かがshared_ptrの非アトミックバージョンを(Boostまたは標準化委員会に)提案したのではないかと思っています(shared_ptrのアトミックバージョンを置き換えるのではなく、共存します)それとともに)そしてそれは特定の理由で撃墜されました。

83
Cornstalks

1. std :: shared_ptrの非アトミックバージョンが利用できるかどうか疑問に思っています

標準では提供されていません。 「サードパーティ」ライブラリによって提供されるものもあるでしょう。確かに、C++ 11以前、およびBoost以前は、誰もが自分自身の参照カウントスマートポインター(自分自身を含む)を書いたようです。

2. 2番目の質問は、C++ 11で非アトミックバージョンのstd :: shared_ptrが提供されなかったのはなぜですか?

この質問は2010年のラッパースウィル会議で議論されました。主題はスイスのNational Body Comment#20によって紹介されました。あなたの質問にあなたが提供したものを含め、議論の両側には強い議論がありました。ただし、ディスカッションの終わりに、shared_ptrの非同期(非アトミック)バージョンを追加することに反対の票が圧倒的に(全会一致ではなく)出されました。

含まれている反対の議論:

  • 非同期のshared_ptrを使用して記述されたコードは、最終的にはスレッド化されたコードで使用され、警告なしにデバッグが困難な問題を引き起こす可能性があります。

  • 参照カウントでトラフィックへの「一方通行」である「ユニバーサル」のshared_ptrを1つ持つことには、次のような利点があります。From 元の提案

    使用される機能に関係なく同じオブジェクトタイプを持ち、サードパーティライブラリを含むライブラリ間の相互運用性を大幅に促進します。

  • アトミックのコストはゼロではありませんが、圧倒的ではありません。コストは、アトミック操作を使用する必要のない移動構築と移動割り当てを使用することで軽減されます。このような操作は、vector<shared_ptr<T>>の消去と挿入で一般的に使用されます。

  • それが本当にやりたいことである場合、人々が独自の非原子参照カウントスマートポインターを書くことを禁止するものは何もありません。

ラッパースヴィルのその日のLWGからの最後の言葉は:

CH 20を拒否します。現時点で変更を行うコンセンサスはありません。

101
Howard Hinnant

ハワードはすでに質問によく答えており、ニコルは、互換性のない多くのポインタではなく、単一の標準の共有ポインタ型を使用することの利点についていくつかの良い点を述べました。

委員会の決定には完全に同意しますが、非同期の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ポリシーを明示的に要求しても、アトミック操作にディスパッチされました。詳細と非アトミックな実装を可能にするGCCへのパッチについては、ポイント(2) http://gcc.gnu.org/ml/libstdc++/2007-10/msg00180.html を参照してください。マルチスレッドアプリでも使用できます。私はそのパッチに何年も座っていましたが、ようやく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>明示的な特殊化を追加することでそれを使用できます(ODR違反を避けるために、特殊化を含めずに__shared_ptr<T, _S_single>をインスタンス化しないようにします)。このようなstd型の特殊化は技術的には定義されていませんが、実際には機能します。この場合、GCCに特殊化を追加するか、独自のコードに追加するかの違いはありません。

49
Jonathan Wakely

2つ目の質問は、C++ 11でstd :: shared_ptrのアトミックバージョンが提供されなかった理由です。 (理由があると仮定)。

侵入ポインタがない理由、または共有ポインタの他の可能なバリエーションがいくつもないのと同じくらい簡単に尋ねることができます。

Boostから受け継がれたshared_ptrの設計は、スマートポインターの最小限の標準的なリンガフランカを作成することです。つまり、一般的に言えば、これを壁から取り外して使用するだけです。これは、さまざまなアプリケーションで一般的に使用されるものです。あなたはそれをインターフェースに置くことができます、そして人々がそれを喜んで使うであろう確率は良いです。

スレッド化は将来的に普及するだけですmore実際、時間が経つにつれ、スレッド化は一般にパフォーマンスを達成するための主要な手段の1つになります。スレッディングをサポートするために必要な最小限のことを行うために基本的なスマートポインターを要求することで、この現実が促進されます。

わずかな変動のある6ダースのスマートポインターを標準にダンプしたり、さらにはポリシーベースのスマートポインターをダンプしたりするのはひどいものでした。誰もが自分の好きなポインタを選び、他のすべてを捨てるでしょう。誰も他の誰ともコミュニケーションが取れないでしょう。それは、C++文字列の現在の状況のようなもので、誰もが独自の型を持っています。さらに悪いことに、文字列との相互運用は、スマートポインタークラス間の相互運用よりもはるかに簡単です。

ブースト、そして委員会の拡張により、使用する特定のスマートポインターを選びました。機能のバランスがよく、実際に広く一般的に使用されていました。

std::vectorは、一部のまれなケースでも、ネイキッド配列と比較していくつかの非効率があります。これにはいくつかの制限があります。一部の用途では、スローアロケータを使用せずに、vectorのサイズにハードリミットを設定する必要があります。しかし、委員会はvectorを誰にとってもすべてのものとなるようには設計しませんでした。ほとんどのアプリケーションで適切なデフォルトになるように設計されています。それが機能しない人は、自分のニーズに合う代替案を書くことができます。

Shared_ptrの原​​子性が負担である場合は、スマートポインターの場合と同様です。次に、それらをあまりコピーしないことを検討することもできます。

22
Nicol Bolas

職場でのshared_ptrについての講演を準備しています。個別のmalloc(make_sharedができることなど)を回避し、上記のshared_ptr_unsynchronizedのようなロックポリシーのテンプレートパラメーターを変更したブーストshared_ptrを使用しています。からプログラムを使用しています

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

テストとして、不要なshared_ptrコピーをクリーンアップした後。プログラムはメインスレッドのみを使用し、テスト引数が表示されます。テスト環境はlinuxmint 14を実行しているノートブックです。これは秒単位の所要時間です。

 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 

'std'バージョンのみが-std = cxx11を使用し、-pthreadはおそらくg ++ __shared_ptrクラスのlock_policyを切り替えます。

これらの数値から、アトミック命令がコード最適化に与える影響を確認します。テストケースはC++コンテナーを使用しませんが、vector<shared_ptr<some_small_POD>>は、オブジェクトがスレッド保護を必要としない場合に影響を受ける可能性があります。追加のmallocがインライン化とコード最適化の量を制限しているため、Boostの影響は少ないと考えられます。

アトミック命令のスケーラビリティをストレステストするのに十分なコアを備えたマシンをまだ見つけていませんが、必要な場合にのみstd :: shared_ptrを使用する方がおそらく良いでしょう。

4
russ

Boostはshared_ptrそれは非アトミックです。それは呼ばれています - local_shared_ptr 、およびブーストのスマートポインターライブラリで見つけることができます。