ここではマルチサイクル方式を想定する
パイプライン処理の何が嬉しい?
RISCの最も大きな利点
→各命令に必要な回路を遊ばせないで済む
→たくさんの命令を一気に実行することによってクロックあたりの命令実行数の効率を擬似的に上げられる
シングルサイクルの1命令/クロックにはならないが、命令を処理し続けることで1命令/クロックに近づけられる
スループットとレイテンシ
- スループット
- 1命令の実行開始から終了までの時間
- スループット
- 単位時間あたりに処理できる仕事量
命令数、ステージ数として、スループット向上比の上限はとなる
ステージ数=パイプラインの段数ぶんスループットを向上できる
→を大きくすればするほど高速化できるか?→NO
どっちにしろ行き着く先は1命令1クロック
→逆にステージ数を増やすのは無意味?→これもNO
ステージ数を増やすと1ステージにかかる処理時間を短縮できる可能性がある
→周波数を上げることができる
→一定時間あたりにさばける処理が増える
とはいえ、ステージ数を増やしすぎてレイテンシを向上できるとは限らない
→レイテンシが悪化したぶんをスループットで取り返せるとも限らない
ノイマン型コンピュータの制約に則ってWB命令の順序などは気にしないといけない
パイプライン処理のためにはキャッシュメモリが必要
ノイマンボトルネック: メモリが遅すぎる(10~200クロックとか)
にどう対応するか
→小容量高速なキャッシュを用意する
- アクセスしそうなものを事前にキャッシュに確保
- すぐにアクセスできる
RISCではなぜパイプライン処理がしやすい?
- 固定長命令なため命令フェッチが簡単になる
- とりあえず4バイト取ればいい
- 命令でコードの処理と同時にソースオペランドレジスタをフェッチできる
- ソースオペランドレジスタ指定になりうるフィールド位置が命令にかかわらず同じ
- とりあえず後ろを見てレジスタを取ってくれば後で使える(使えなければ値を破棄する)
- ソースオペランドレジスタ指定になりうるフィールド位置が命令にかかわらず同じ
- メモリオペランドを持つのはloadとstoreのみ
- 実行アドレス計算をするのはこの命令のみ
- 1つの加算機を実行アドレス計算と算術演算で共用できる
- 算術演算命令のオペランドとしてメモリを指定できると1命令で2回加算命令を行う必要性がある
- IDでのレジスタに対するフェッチとライトは同一クロック内で実行できる
- 実行アドレス計算をするのはこの命令のみ
パイプラインを実現する
シングルサイクル方式の回路
graph LR R(5クロックごとに命令)-->A A[IF]-->B[ID] B-->C[EX] C-->D[MA] D-->E[WB]
これを書き換えていく
graph LR R(1クロックごとに命令)-->A A[IF]-->F[レジスタ]-->B[ID] B-->G[レジスタ]-->C[EX] C-->H[レジスタ]-->D[MA] D-->I[レジスタ]-->E[WB]
次ステージの回路はパイプラインレジスタを読み書きしてデータや情報をやり取りする
ハザード
何らかの理由であるステージの処理が予定通りに完了しない
→後続ステージを待たせる(パイプラインをストールさせる)のでパイプラインに穴、スループットの低下
可能な限りストールを回避して低減したい
構造ハザード
- 計算機のアーキテクチャによるハザード
同時に実行する命令に対してハードウェア資源が足りなくなってしまう状態
選考命令を先に動かすために後続命令を一気に後ろに1ステージずらす
例: MAとIFで同時にメモリアクセスが発生する
→流石にまずいのでアーキテクチャレベルで対処する
ハーバードアーキテクチャによる対処
命令を置くメモリとデータを置くメモリを分離する
→現代だとメモリは1つでキャッシュを命令とアドレスに分割
整数の上除算や浮動小数点演算は遅いのでEXステージが伸びる
→ストールか大幅な周波数の低下
→不動小数点ユニットが1つなら後続の同演算命令がストールされる
対処
- 複数のユニットを用意する
- 演算ユニットをパイプラインにする
MIPSではEXステージでmultなどを別の回路に投げて、パイプラインはそのまま続行する
→結果が必要になったらそのときに初めてストールする
キャッシュミス
当該データがメインメモリからキャッシュメモリへ転送される間ストール
制御ハザード
beqなどの分岐命令によって発生するハザード
- 分岐する倍は分岐先の命令に行くが、後続命令を行う場合もある
- 次命令を決めないことには命令フェッチをできない
遅延分岐による対処
MIPSでは分岐命令の直後に遅延スロットとして分岐結果によらず必ず実行される命令を差し込む(命令スケジューリング)
ほとんどの場合はストールさせずに条件結果を予測して自命令と期待される命令を投機的に実行(whileならある程度の期間回るだろう、とか)
うまくいかなかったら破棄する
データハザード