2番目のスニペットではj
、各反復での変化により、空間的な局所性が低いパターンが生成されます。舞台裏では、配列参照が以下を計算することを忘れないでください。
( ((y) * (row->width)) + (x) )
配列の50行だけに十分なスペースがある単純化されたL1キャッシュについて考えてみます。最初の50回の反復では、50回のキャッシュミスに対して避けられないコストを支払うことになりますが、その後はどうなりますか?50から99までの反復ごとに、ミスをキャッシュし、L2(および/またはRAMなど)からフェッチする必要があります。次に、x
1に変更しy
て最初からやり直し、配列の最初の行がキャッシュから削除されたため、別のキャッシュミスが発生します。
最初のスニペットにはこの問題はありません。これは、行優先の順序で配列にアクセスします。これにより、ローカリティが向上します。キャッシュミスの料金を支払う必要があるのは、行ごとに最大1回(ループの開始時に配列の行がキャッシュに存在しない場合)だけです。
そうは言っても、これはアーキテクチャに非常に依存する質問であるため、結論を出すには、詳細(L1キャッシュサイズ、キャッシュラインサイズなど)を考慮する必要があります。また、両方の方法を測定し、ハードウェアイベントを追跡して、結論を引き出すための具体的なデータを入手する必要があります。