メッシュや画像処理、物理エンジン、レイトレーシングなどのパフォーマンスが重要なフィールドで機能するリンクリストで最も役立つケースの1つは、リンクリストを使用すると、参照の局所性が実際に向上し、ヒープ割り当てが減少し、場合によってはメモリ使用量が減少することもあります。簡単な選択肢。
これは、リンクリストがその逆を頻繁に行うことで悪名高いため、それをすべて実行できる完全な矛盾のように見えるかもしれませんが、各リストノードには固定サイズと配置要件があるという点で固有のプロパティがあり、これを利用してそれらは隣接して格納され、可変サイズのものではできない方法で一定時間に削除されます。
結果として、100万のネストされた可変長サブシーケンスを含む可変長シーケンスを格納することと同様のことを実行したい場合を考えてみましょう。具体的な例は、100万のポリゴン(一部の三角形、一部の四角形、一部の五角形、一部の六角形など)を格納するインデックス付きメッシュであり、メッシュ内の任意の場所からポリゴンが削除されたり、ポリゴンが再構築されて既存のポリゴンに頂点を挿入したり、 1つ削除します。その場合、100万のtinyを格納するとstd::vectors
、すべてのベクトルへのヒープ割り当てと、爆発的なメモリ使用に直面することになります。100万タイニーSmallVectors
は、一般的な場合ほどこの問題に悩まされることはありませんが、個別にヒープに割り当てられていない事前割り当てされたバッファーは、爆発的なメモリ使用を引き起こす可能性があります。
ここでの問題は、100万のstd::vector
インスタンスが100万の可変長のものを格納しようとすることです。可変長のものは、連続して非常に効果的に格納できず、コンテンツをヒープの他の場所に格納しなかった場合、一定の時間で(少なくとも非常に複雑なアロケーターなしで簡単に)削除できないため、ヒープ割り当てが必要になる傾向があります。
代わりに、これを行う場合:
struct FaceVertex
{
// Points to next vertex in polygon or -1
// if we're at the end of the polygon.
int next;
...
};
struct Polygon
{
// Points to first vertex in polygon.
int first_vertex;
...
};
struct Mesh
{
// Stores all the face vertices for all polygons.
std::vector<FaceVertex> fvs;
// Stores all the polygons.
std::vector<Polygon> polys;
};
...次に、ヒープ割り当てとキャッシュミスの数を劇的に減らしました。アクセスするすべてのポリゴンに対してヒープ割り当てと潜在的に強制的なキャッシュミスを要求する代わりに、メッシュ全体に格納された2つのベクトルの1つが容量(償却コスト)を超えた場合にのみヒープ割り当てを要求するようになりました。また、ある頂点から次の頂点に到達するためのストライドによって、キャッシュミスのシェアが引き続き発生する可能性がありますが、ノードが隣接して格納され、隣接する頂点が格納される可能性があるため、すべてのポリゴンが個別の動的配列を格納する場合よりも少ないことがよくあります。立ち退きの前にアクセスする(特に、多くのポリゴンが頂点を一度に追加するため、ライオンのポリゴンの頂点の割合が完全に連続することを考慮します)。
次に別の例を示します。
...グリッドセルを使用して、たとえば、1フレームごとに移動する1600万個のパーティクルのパーティクル間衝突を加速します。そのパーティクルグリッドの例では、リンクリストを使用して、3つのインデックスを変更するだけで、あるグリッドセルから別のセルにパーティクルを移動できます。ベクトルから消去して別のベクトルにプッシュバックすると、かなりのコストがかかり、ヒープ割り当てが増える可能性があります。リンクリストは、セルのメモリを32ビットに減らします。ベクトルは、実装に応じて、空のベクトルに32バイトを使用できるポイントに動的配列を事前に割り当てることができます。グリッドセルが約100万個ある場合、それはかなりの違いです。
...そしてここで私は最近リンクリストが最も役立つと思います。特に32ビットインデックスは64ビットマシンのリンクのメモリ要件を半分にし、それらがノードは配列に連続して格納されます。
多くの場合、それらをインデックス付きのフリーリストと組み合わせて、どこでも一定の時間で削除および挿入できるようにします。
その場合、next
ノードが削除されている場合、インデックスは次の空きインデックスを指すか、ノードが削除されていない場合、次に使用されるインデックスを指します。
そして、これは最近私がリンクされたリストで見つける一番のユースケースです。たとえば、それぞれ4つの要素(たとえば、要素が削除され、これらのサブシーケンスの1つに追加されている場合もある)を平均化する100万の可変長サブシーケンスを保存する場合、リンクされたリストにより、400万を保存できます。リンクされたリストノードは、それぞれ個別にヒープ割り当てされた100万のコンテナではなく、連続して1つの巨大なベクトル、つまり100万の小さなベクトルではありません。