同じ件数を読む処理でも、メモリを読む順番でキャッシュの効き方が変わる
CPUはメモリをキャッシュラインというかたまりで取り込む。近くのデータを続けて使う空間的局所性と、同じデータを短い間に再利用する時間的局所性を生かすと、下位のメモリへのアクセスを減らしやすい。
用語
- キャッシュライン
- CPUキャッシュがデータを取り込む単位。1要素だけを要求しても、通常はその周辺を含むかたまりで取り込む。大きさは構成による。
- 空間的局所性
- あるアドレスを使った直後に、その近くのアドレスも使う性質。連続配列の順次走査が例。
- 時間的局所性
- 同じデータを短い間に再び使う性質。再利用までキャッシュに残っていれば、下位への読み取りを減らせる。
- キャッシュミス
- 調べたキャッシュに必要なデータがないこと。下位のキャッシュや主記憶を読み、取得の時間を払う。
図と例
- 8バイト要素を隣へ順に読む: 1ライン
- 64バイトおきの要素を読む: 8ライン
計算モデル:ラインを64バイト、要素を8バイトとし、先頭をライン境界へそろえた連続配列を読む。キャッシュは初め空で、プリフェッチと他のアクセスは無視する。先頭8要素は1ラインに収まる。64バイトずつ離れた8要素なら8ラインにまたがる。読む要素は同じ8件でも、触れるラインは1本と8本になる。これはライン数の比較で、8倍速くなる実測ではない。
実際のサービス・製品では
Cornell Virtual Workshop
Cornellの性能最適化教材は、Intelプロセッサの64バイトラインを例に、連続したアドレスの利用とキャッシュ内のデータの再利用を説明する。このTipの64バイトは、その例に合わせた仮定である。
資料を読む
解説
大量の配列処理や集計で、計算量を改善しても遅いときに使う。連続配置の配列を順に読む、小さなブロックへ分けて再利用するなどを比べる。意味上隣り合う要素が物理的にも連続しているとは限らないため、言語とデータ構造の配置を確認する。速度は実測し、ラインの使用率をそのまま高速化の倍率にしない。
CPUの近くには、主記憶より小さく高速なCPUキャッシュがある。欲しい値がなくてキャッシュミスすると、下位のキャッシュや主記憶から、その値を含むキャッシュラインを取り込む。配列を隣の要素へ順に読むと、一度取り込んだラインの残りも使えるため空間的局所性が高い。同じ小さなデータを何度も使う処理には時間的局所性がある。一方、離れた場所へポインターで次々に飛ぶと、取り込んだラインの一部しか使わないことがある。どちらもO(n)の走査でも、メモリアクセスの費用は同じとは限らない。実際の差は配列の配置・キャッシュ容量・プリフェッチ・コンパイラーなどで変わる。
トレードオフ
- 連続した配列を順に走査する
- 得るもの:隣の要素を同じラインから使いやすい
- 払うもの:要素の挿入や順序の変更に、移動や別の索引が必要になる場合がある
- ポインターで要素をつないでたどる
- 得るもの:場所を確保した要素のつなぎ替えを表現しやすい
- 払うもの:配置が離れていると空間的局所性が低くなり、ポインター自体の領域も使う
さらに学ぶ
チェック
計算モデル:64バイトのキャッシュライン、8バイト要素、配列先頭はライン境界に一致。先頭から連続した16要素は何本のラインに収まる?
- 1ライン
- 2ライン
- 16ライン
解答と解説
2ライン
16要素×8バイト=128バイトなので、128÷64=2ライン。境界にそろっている条件も使っている。これは占めるライン数の計算であり、実行時間やミス回数はキャッシュの初期状態やプリフェッチなどでも変わる。