6

私は割り当て方法を単純化することから、新しいコードのオーバーライドを複数のアロケータに変更することを研究しています。しかし、どのように効率的に複数のアロケータを使用できますか?私の研究を通して考案できる唯一の方法は、アロケータをグローバルにすることでした。しかし、これには問題があるように思えました。なぜなら、多くのグローバルを使用するのは一般的に「悪い考えです」。複数のアロケータを効率的に使用する

複数のアロケータを効率的に使用する方法を探しています。たとえば、特定のサブシステムに対してのみ1つのアロケータを使用し、別のサブシステムに対して異なるアロケータを使用することができます。これを行う唯一の方法が複数のグローバルアロケータを使用するかどうかはわかりませんので、より良い洞察とデザインが望まれます。

+2

なぜアロケータはグローバルでなければならないのですか?割り当てられた各ユニットが独自のアロケータへの参照を持ち、正しく解放できる場合、アロケータが実際にどこにあるかは重要ですか? –

+0

アロケータは、割り当てられたユニットのどこに残しますか?あたかもそれがグローバルでなければならないかのように思えます。 – chadb

答えて

8

C++ 2003では、アロケータモデルが壊れていて、本当に適切な解決策はありません。 C++ 2011の場合、アロケータモデルは固定されています。インスタンスアロケータは、含まれているオブジェクトに伝播することができます(ただし、それらを置き換えることを選択しない限り)。一般的には、これを有効にするには、デフォルトのstd::allocator<T>が必須ではない動的多型アロケータ型を使用したいと思うかもしれません(そして、これはより良い実装の選択肢かもしれませんが、一般的には動的多型ではないと思います)。しかし、メモリ割り当てを行う標準C++ライブラリのすべてのクラスは、テンプレート引数としてアロケータ型を取るテンプレートです(例えば、IOStreamsは例外ですが、一般的に、アロケータサポートの追加を保証するために興味深い量のメモリを割り当てません) )。

あなたのコメントの中には、アロケータが効果的にグローバルになる必要があることが主張されています。これは間違いなく間違いです。各アロケータ対応型は与えられたアロケータのコピーを格納します(少なくとも、インスタンスレベルのデータを持っている場合は、operator new()operator delete()を使用するデフォルトのアロケータの場合のように何も格納されていない場合) 。これは、事実上、アロケータを使用するアロケータが存在する限り、オブジェクトに割り当てられたアロケーションメカニズムが固執する必要があることを意味します。このをグローバルオブジェクトを使用して行うことができますが、グローバルオブジェクトを使用して行うこともできます。アロケータをそれが与えられた全てのオブジェクトを含むオブジェクトに関連付けることを含む。たとえば、XML、Excel、Pagesなどあらゆる構造ファイルがメンバーにアロケータを渡すと考えると、アロケータはそのドキュメントのメンバーとして生きることができ、すべてのコンテンツが破棄された後にドキュメントが破棄されると破棄されます。アロケータモデルのこの部分は、アロケータ引数を取る限り、pre-C++ 2011クラスでも動作するはずです。ただし、C++ 2011以前のクラスでは、アロケータは含まれたオブジェクトに渡されません。たとえば、アロケータにstd::vector<std::string>を指定すると、に割り当てられたアロケータを使用してstd::stringに適切に変換されたC++ 2011バージョンでは、std::stringが作成されます。これは、C++ 2011以前のアロケータでは発生しません。

サブシステムで実際にアロケータを使用するには、関数やクラスへの引数として明示的に渡すか、コンテキストとして機能するアロケータ対応オブジェクトによって暗黙的に渡す必要があります。たとえば、標準コンテナのいずれかをコンテキストの一部として使用する場合は、get_allocator()メソッドを使用して使用済みアロケータを取得できます。

+0

非常に興味深いですが、私は参照カウントを考えていませんでした。私自身のアロケータを作成する予定なので、weak_referenceのようなものから継承して参照カウントを可能にするべきですか? – chadb

+1

個人的には、 'my_allocator'アロケータのメンバーとして' std :: shared_ptr 'を使います。実際の割り当てロジックは、 'my_allocation_base'から派生したクラスに配置されます。割り振りアプローチが1つしかない場合は、論理をオブジェクトに指し示すことができます。 'weak_reference'を使うと(それが何であるかわからない)、うまく動作しないかのように聞こえます:割り当てられたメモリを保持しているすべてのオブジェクトから割り当てオブジェクトへの実際の参照を持ち、リリースされました。 –

2

new配置を使用できます。これは、メモリ領域を指定するか、またはタイプのstatic void* operator new(ARGS)をオーバーロードするために使用できます。グローバルは必須ではなく、効率が重要で問題が厳しい場合は、ここでは悪い考えです。もちろん、1つまたは複数のアロケータを保持する必要があります。

あなたの問題を理解し、プログラム内のパターンと実際の使用状況に基づいてアロケータの戦略を作成することが最善の方法です。汎用目的のmallocは、それが行う機能に非常に優れているので、常にそれを測定する1つのベースラインとして使用してください。あなたの使用パターンがわからない場合、アロケータはおそらくmallocより遅くなるでしょう。

標準コンテナにグローバルまたはスレッドのローカルおよびカスタムアロケータを使用しない限り、これらのタイプを使用すると標準コンテナとの互換性が失われることに注意してください。また、独自のアロケータとコンテナを記述することもできます。

+0

アロケータはどのように開催されるべきですか?あなたはグローバルにすべきではないと言いましたが、その時はどこにすべきですか? (また、私はすでに自分の持っている標準コンテナとの互換性には関心がありません)。 – chadb

+0

@chadb問題によって異なります。メンバー参照(例:参照カウント割り当て)と外部参照(たとえば、ノードがすべて1つのアロケータによって管理されるグラフのアロケータ)の両方を使用します。スレッド・アロケータ(スレッドまたはそのデータによってアクセスされるスレッド・アロケータ)は、別のアプローチですが、その場合は外部参照が優先されます。 – justin

+0

サブシステム全体(そのシングルトンにないルート)で使用されるアロケータはどうでしょうか?それは、私が信じている看板で述べたように、現時点での私の主なユースケースと思われます。そのためのアロケータはどこに格納されますか?私はそこにいるグローバルな人以外には何の事例も考えられません。 – chadb

1

複数のアロケータの用途には、CPU使用率の低下、断片化の縮小、キャッシュミスの減少などがあります。したがって、ソリューションは実際にはどのタイプとどこに割り振りのボトルネックがあるかによって異なります。

アクティブスレッドのロックレスヒープを使用して同期を取り除くことで、CPU使用率が向上します。これは、スレッドローカルストレージを持つメモリアロケータで実行できます。

異なるヒープから異なる寿命の割り当てを割り当てることで断片化が改善されます - ユーザーのアクティブなタスクとは別のヒープにバックグラウンドIOを割り当てることで、2つが互いに混乱しないようにします。これはおそらくヒープ用のスタックを持つことと、異なる機能スコープにいるときにプッシュ/ポップすることによって実現されます。

キャッシュミスは、システム内の割り当てをまとめて維持することによって改善されます。 Quadtree/Octreeの割り振りを独自のヒープから取得することで、俯瞰的なクエリに局所性があることが保証されます。これは、特定のクラス(OctreeNode)の演算子newと演算子deleteをオーバーロードすることによって最も効果的です。

+0

多分何かを誤解したかもしれませんが、私の質問は主に複数のアロケータを使用する方法であり、なぜそうではありませんでした。 – chadb

関連する問題