2

私はここでそれを読みました:

make_sharedは、参照制御ブロックを実際のオブジェクトと一緒に1つの動的割り当てで割り当てるため、(実際には)より効率的です。対照的に、ネイキッドオブジェクトポインタを受け取るshared_ptrのコンストラクタは、参照カウントに別の動的変数を割り当てる必要があります

std::make_sharedを使用して作成されたstd::shared_ptrのベクトルは、データ(制御ブロックと実際のポインターのデータ)が1つのチャンクにあるため、「キャッシュフレンドリー」になるということですか?

私のユースケースは、オブジェクトが指す100000個の共有ポインタのベクトルが14バイトです。

4

3 に答える 3

1

で作成された共有ポインタのベクトルを作成することはできませんmake_shared。それを試してみてください、あなたはそれを行うことはできません。あなたができる最善のことは、で作られた共有ポインタからベクター内のポインタをコピーコンストラクトまたはコピーアサインすることmake_sharedです。しかし、その後、それらはメモリ内の別の場所になります。

ただし、制御ブロックは引き続きオブジェクトの近くにあります。を呼び出すとmake_shared、実際には3つのものが作成されます。オブジェクト、オブジェクトへの参照を追跡するための共有ポインター制御ブロック、および共有ポインターです。このmake_shared関数により、制御ブロックとオブジェクト自体が単一の連続したメモリブロックに割り当てられます。

それがキャッシュフレンドリーかどうかは興味深い質問です。基本的には、オブジェクトの使用方法によって異なります。

共有ポインターのみを頻繁に操作し、それらが指すオブジェクトを操作しない場合(たとえば、ベクトルを複製して各共有ポインターの参照カウントを増やす)、個別の割り当ては、結合されたものではなく、おそらくキャッシュに適しています。それはあなたにmake_share与えます。

共有ポインタを操作するたびにオブジェクト自体を頻繁に操作する場合は、make_shared通常の状況ではキャッシュに適している必要があります。

于 2012-10-11T11:54:53.647 に答える
1

たぶん、しかしそれを当てにしないでください。

キャッシュを使いやすくするために、使用するメモリをできるだけ少なくし、アドレスが接近している操作も時間的に接近している(つまり、2番目の操作がまだある程度のメモリを使用するように十分に接近している)必要があります。最初の操作の効果からのキャッシュの割合:キャッシュのレベルが低いほど良い)。

を使用すると、合計メモリ使用量がわずかに節約される可能性があります。これは、メモリ使用量のパターンに関係なく、make_shared少なくともキャッシュのメリットになる傾向があります。

を使用するmake_sharedと、制御ブロックと参照されるオブジェクト(referand)がメモリ内で隣接します。

を使用せずmake_shared、オブジェクトのサイズがコントロールブロックと異なる場合、一般的なメモリアロケータを使用すると、オブジェクトが1つの場所にクラスター化され、コントロールブロックが別の場所にクラスター化される可能性が高くなります。それら同じサイズである場合(実装固有の方法でメモリアロケータによって丸められると)、一般的なメモリアロケータでは、shared_ptrそれに影響を与える何かがない限り、長期間メモリ内で交互になる可能性があります。

メモリアクセスパターンによって、これらのレイアウトのどれがキャッシュに適しているかが決まります。もちろん、make_shared実装の詳細によっては、ケース以外で取得する実際のレイアウトが別のものになる場合があります。

オブジェクトが制御ブロックおよび参照ノードから分離されているため、aがあるという事実は、vector基本的にこれらすべてから独立しています。shared_ptr

于 2012-10-11T12:52:57.760 に答える
-1

上記のポスターで述べたように、make_sharedを使用してオブジェクトを作成すると、参照されるオブジェクトに隣接する「制御ブロック」が作成されます。

しかしあなたの場合、これは悪い選択だと思います。

メモリを割り当てる場合、大きなブロックであっても、まばらで断片化されたページ割り当てとは対照的に、連続した「物理スペース」を取得する保証はありません。このため、リストを反復処理すると、制御構造(データを指す)を取得するためだけに、メモリの広いスパンにわたって読み取りが発生します。

「しかし、私のキャッシュラインは64バイトの長さです!」あなたは言う。これが当てはまる場合、 「これは、オブジェクトが制御構造とともにキャッシュにロードされることを意味します」と思うかもしれませんが、必ずしもそうとは限りません。これは、データアライメント、キャッシュラインサイズ、キャッシュの関連付け、実際に使用するメモリ帯域幅など、さまざまな要因によって異なります。

あなたが遭遇する問題は、データがどこにあるかを把握するために最初に制御構造をフェッチする必要があるという事実です。代わりに、それはすでにキャッシュに存在する可能性があるため、データの一部(制御構造)は少なくともmake_sharedを使用する代わりに、それらをすべて一緒に割り当てると、実質的にキャッシュ内にあることが保証されます。

データをキャッシュに適したものにしたい場合は、データへのすべての参照が可能な限り最高レベルのキャッシュ内に収まるようにする必要があります。引き続き使用すると、キャッシュにとどまるようにするのに役立ちます。キャッシュアルゴリズムは、コードが非常にブランチが多い場合を除いて、データのフェッチを処理するのに十分なほど洗練されています。これは、データを「キャッシュフレンドリー」にするためのもう1つの部分です。データを操作するときは、使用するブランチをできるだけ少なくします。

また、作業するときは、キャッシュに収まるように分割してください。可能であれば、一度に32kでのみ動作します。これは、最新のプロセッサでは控えめな数値です。コードを実行するCPUが正確にわかっている場合は、必要に応じて、それほど保守的に最適化することはできません。

編集:私は適切な詳細に言及するのを忘れました。最も頻繁に割り当てられるページサイズは4kです。特にローエンドプロセッサでは、キャッシュは「連想的」であることがよくあります。2ウェイアソシアティブとは、メモリ内の各場所を他のすべてのキャッシュエントリにのみマップできることを意味します。4ウェイアソシアティブは、4つの可能なマッピングのいずれかに適合できることを意味し、8ウェイは、8つの可能なマッピングのいずれかに適合できることを意味します。プロセッサ上の最速のキャッシュ(L1)は、必要な制御ロジックが少ないため、関連付けが最も少なくなる傾向があります。参照するデータの連続ブロック(連続制御構造など)を持つことは良いことです。完全に関連付けられたキャッシュが望ましいです。

于 2012-10-11T13:12:06.533 に答える