プリフェッチとスカウトスレッド

メモリアクセスがキャッシュをミスして下位のキャッシュやメインメモリにアクセスする必要が出ると、長時間のストールが発生して性能が低下してしまう。

これを避けるために、必要となるデータを予めメモリからキャッシュに持ってくるのがprefetch命令である。最近のプロセサはprefetch命令を備えており、コンパイラは本当にデータが必要となるよりも前にprefetch命令を発行してメモリアクセスを開始する。そうするとデータを必要とするロード命令を実行したときには、キャッシュにデータが到着しており、メモリレイテンシを隠蔽することができる。

しかし、コンパイラが静的にプログラムを解析して、最適にprefetch命令を挿入するのは難しい面があり、マルチスレッドを利用して、一方のスレッドは先行してメモリのプリフェッチを行い、少し遅れて動作する本当の処理を行うスレッドがデータを必要とするときには、キャッシュにデータが到着しているというようにできないかという研究が行われている。

その1つの具体化が、SunのRockプロセサの「Scout Thread」である。RockはScout(偵察)動作モードでは、L1D$のミス、uDTLBミスや割り算命令などの時間が掛かる命令に遭遇すると、実行中のスレッドのアーキテクチャレジスタの状態をチェックポイントとしてフリーズし実行を停止する。

そして、このアーキテクチャレジスタの状態をペアとなるスカウトスレッドのアーキテクチャレジスタにコピーし、スカウトスレッドは時間の掛かる命令が終わったかのように先の命令の実行を続けていく。これは、ソフトウェアの用語で言うとforkに相当する。

スカウトスレッドの動作を、発表における説明を元に筆者の推定を含めて作ったのが次の図10.6である。命令nがアドレスAのデータをロードするLD A命令で、このロードがキャッシュをミスするとレーテンシの長い命令となるので、スカウトスレッドのforkが行われる。メインスレッドは命令nの完了を待っているが、スカウトスレッドは命令n+1から命令の実行を続ける。そして、命令n+1はアドレスBに対するロード命令であり、これもキャッシュミスをしても、スカウトスレッドはLD B命令の完了を待たず、命令n+2、n+3と実行を続けると思われる。

そして、メインスレッドが命令nのLD A命令の実行を完了すると、スカウトスレッドの実行を停止して、メインスレッドに一本化するjoinを行う。スカウトスレッドが実行した命令は、一見、無駄のようであるが、この図のように、LD A命令の待ちの間に、LD B命令の実行によるメモリアクセスが開始されており、メインスレッドが命令n+1を実行する時点では、すでにアドレスBのデータがキャッシュに到着しているか、到着していない場合でも、少ない待ち時間でデータが到着する。

図10.6 スカウトスレッドの動作

このようにスカウトスレッドを使うと、シミュレーションによるTPCCベンチマークの実行性能の評価では、40% IPC(Instruction Per Cycle)が向上したという。また、これは、スカウトOffの状態でL2$の量を1MBから8MBに増加したのと同じ性能向上であるという。