リストにはひどい (存在しない) キャッシュの局所性があります。すべてのノードは新しいメモリ割り当てであり、どこにでもある可能性があります。したがって、あるノードから次のノードへポインタをたどるたびに、メモリ内の新しい無関係な場所にジャンプします。そして、はい、それはパフォーマンスをかなり損ないます。キャッシュ ミスは、キャッシュ ヒットよりも 2 桁遅くなる場合があります。vector または deque では、ほぼすべてのアクセスがキャッシュ ヒットになります。ベクトルはメモリの 1 つの連続したブロックであるため、それを反復処理するのは可能な限り高速です。両端キューはメモリのいくつかの小さなブロックであるため、キャッシュ ミスが発生することがありますが、それでもまれであり、ほとんどのキャッシュ ヒットが得られるため、反復は依然として非常に高速です。
リストは、ほとんどすべてのキャッシュ ミスになります。そして、パフォーマンスは最悪です。
実際には、リンクされたリストがパフォーマンスの観点から正しい選択になることはほとんどありません。
編集:コメントが指摘したように、リストのもう1つの問題はデータの依存関係です。最新の CPU は、操作をオーバーラップするのが好きです。しかし、次の命令がこの命令の結果に依存している場合、それはできません。
ベクトルを反復処理している場合、それは問題ありません。メモリをチェックインすることなく、オンザフライで読み取る次のアドレスを計算できます。現在address を読んでいる場合x
、次の要素は address に配置されますx + sizeof(T)
。ここで、T は要素の型です。そのため、そこには依存関係がなく、CPU は次の要素またはその次の要素の読み込みをすぐに開始できますが、前の要素を処理している間もそのままです。そうすれば、データは必要なときにすぐに利用できるようになります。これにより、RAM 内のデータにアクセスするコストをさらに隠すことができます。
i
リストでは、ノードからノードへのポインターをたどる必要があり、ロードされるi+1
までi+1
、どこを探すべきかさえわかりませんi+2
。データ依存性があるため、CPU は一度に 1 つずつノードを読み取る必要があり、将来のノードがどこにあるかをまだ認識していないため、事前に読み取りを開始することはできません。
リストのすべてがキャッシュ ミスでなかった場合、これは大きな問題にはなりませんでしたが、多くのキャッシュ ミスが発生しているため、これらの遅延はコストがかかります。