コンピュータ工学において、命令パイプライン処理とは、単一のプロセッサ内で命令レベルの並列処理を実現するための技術である。パイプライン処理は、入力された命令を一連の連続したステップ(「パイプライン」と呼ばれる)に分割し、異なるプロセッサユニットが命令の異なる部分を並列処理することで、プロセッサのすべての部分を何らかの命令で常に稼働させようとするものである。
パイプライン方式のコンピュータでは、命令は中央処理装置(CPU)内を段階的に通過します。例えば、フォン・ノイマン・サイクルの各ステップ(命令のフェッチ、オペランドのフェッチ、命令の実行、結果の書き込み)ごとに1つのステージが設けられる場合があります。パイプライン方式のコンピュータでは、通常、各ステージの後に「パイプラインレジスタ」が設けられています。これらのレジスタには、命令や計算結果の情報が格納され、次のステージの論理ゲートが次のステップを実行できるようになります。
この構成により、CPUはクロックサイクルごとに1つの命令を実行できます。偶数段は方形波クロックの一方のエッジで動作し、奇数段はもう一方のエッジで動作するのが一般的です。これにより、同じクロックレートであればマルチサイクルコンピュータよりもCPUのスループットが向上しますが、パイプライン処理自体のオーバーヘッドが増加するため、レイテンシが増加する可能性があります。また、電子ロジックの最大速度は固定されていますが、パイプラインコンピュータはパイプライン内の段数を変更することで高速化または低速化できます。段数が増えると、各段の処理量が少なくなり、ロジックゲートによる遅延が減るため、より高いクロックレートで動作させることができます。
パイプライン方式のコンピュータは、コストを1秒あたりの命令あたりの論理ゲート数で測ると、多くの場合最も経済的です。各瞬間において、命令は1つのパイプラインステージにしか存在せず、平均的にパイプラインステージのコストはマルチサイクルコンピュータよりも低くなります。また、適切に設計されていれば、パイプライン方式のコンピュータの論理回路の大部分は常に使用されています。対照的に、アウトオブオーダー方式のコンピュータでは、通常、どの瞬間においても大量のアイドル状態の論理回路が存在します。同様の計算結果からも、パイプライン方式のコンピュータは命令あたりのエネルギー消費量が少ないことが示されています。
しかし、パイプライン型コンピュータは、同等のマルチサイクル型コンピュータに比べて、一般的に複雑で高価です。通常、より多くの論理ゲート、レジスタ、そしてより複雑な制御ユニットを備えています。同様に、総エネルギー消費量は多くなりますが、命令あたりのエネルギー消費量は少なくなります。アウトオブオーダー型CPUは、一度に複数の命令を実行できるため、通常、1秒あたりの命令実行数が多くなります。
パイプライン型コンピュータでは、制御ユニットがプログラムの指示に従って処理の流れを開始、継続、停止させます。命令データは通常、パイプラインレジスタを介して各ステージに渡され、各ステージにはそれぞれ独立した制御ロジックが存在します。制御ユニットはまた、各ステージの命令が他のステージの命令の動作を妨げないようにします。例えば、2つのステージが同じデータを使用する必要がある場合、制御ロジックはそれらの使用が正しい順序で行われるようにします。
パイプライン処理を行うコンピュータは、効率的に動作している場合、各ステージに1つの命令を持ちます。そして、それらの命令すべてを同時に処理します。クロックサイクルごとに約1つの命令を完了できます。しかし、プログラムが別の命令シーケンスに切り替えると、パイプラインは処理中のデータを破棄して再起動しなければならない場合があります。これを「ストール」と呼びます。
パイプライン型コンピュータの設計の多くは、各ステージ間の干渉を防ぎ、停止時間を低減する。
依存するステップの数は、マシンのアーキテクチャによって異なります。例えば、次のようになります。
パイプラインが「深く」なる(依存するステップの数が増える)につれて、特定のステップはより単純な回路で実装できるようになり、プロセッサのクロックを高速化できる可能性がある。[ 3 ]このようなパイプラインはスーパーパイプラインと呼ばれることがある。[ 4 ]
プロセッサは、毎サイクルで命令をフェッチできる場合に、完全パイプライン化されていると言われます。したがって、一部の命令や条件によって新しい命令のフェッチが阻害されるような遅延が発生する場合、そのプロセッサは完全パイプライン化されていません。
パイプライン処理の先駆的な使用例はILLIAC IIプロジェクトとIBM Stretchプロジェクトであったが、それ以前の1939年のZ1と1941年のZ3では単純なバージョンが使用されていた。[ 5 ]
パイプライン処理は、1970年代後半にベクトルプロセッサやアレイプロセッサなどのスーパーコンピュータで本格的に始まりました。初期のスーパーコンピュータの1つは、コントロールデータコーポレーションが開発したサイバーシリーズです。その主要設計者であるシーモア・クレイは、後にクレイ・リサーチを率いました。クレイは、乗算と加算/減算の両方の機能にパイプライン処理を使用したXMPシリーズのスーパーコンピュータを開発しました。その後、スターテクノロジーズは、ロジャー・チェンが開発した並列処理(複数のパイプライン処理された機能を並列に実行すること)を追加しました。1984年には、スターテクノロジーズはジェームズ・ブラッドリーが開発したパイプライン除算回路を追加しました。
パイプライン処理はスーパーコンピュータに限られたものではありませんでした。1976年、Amdahl Corporationの汎用メインフレーム470シリーズには7ステップのパイプラインと特許取得済みの分岐予測回路が搭載されていました。RISC設計思想の中核要素であるパイプライン処理は、1980年代半ばまでに、開発者によって従来のCISCアーキテクチャにも導入されるようになりました。[ 6 ] [ 7 ]
逐次実行モデルでは、各命令が完了するまで次の命令は開始されないと仮定されていますが、パイプラインプロセッサではこの仮定は成り立ちません。期待される結果が問題となる状況をハザードと呼びます。架空のプロセッサに対する次の2つのレジスタ命令を考えてみましょう。
1: R5に1を加える 2:R5をR6にコピーする
プロセッサが最初の図(記事冒頭の「基本的な5段階パイプライン」)に示されている5つのステップを持っている場合、命令1は時刻t1でフェッチされ、 t5で実行が完了します。命令2はt2でフェッチされ、 t6で完了します。最初の命令は、t5で5番目のステップ(レジスタライトバック)として、インクリメントされた数値をR5に格納する可能性があります。しかし、 2番目の命令は、時刻t3で2番目のステップ(命令デコードとレジスタフェッチ)で、R5から数値を取得(R6にコピー)する可能性があります。この時点では、最初の命令は値をインクリメントしていないようです。上記のコードはハザードを引き起こします。
コンパイル言語でコンピュータプログラムを作成する場合、コンパイラが危険を回避する機械語を生成するように設計できるため、これらの懸念は生じないかもしれない。
初期のDSPおよびRISCプロセッサの中には、隣接命令やほぼ隣接命令(遅延スロットと呼ばれる)におけるこのような依存関係を避けるようプログラマに指示したり、2番目の命令が目的の値ではなく古い値を使用することを宣言したり(上記の例では、プロセッサが直感に反してインクリメントされていない値をコピーする可能性がある)、使用する値が未定義であることを宣言したりするドキュメントが存在するものもある。プログラマは、その間にプロセッサが実行できる無関係な作業を持っている場合や、正しい結果を保証するために、パイプライン処理の利点を部分的に打ち消す形で、コードにNOPを挿入する場合がある。
パイプラインプロセッサは、プログラマが各命令が次の命令の開始前に完了することを前提としている場合に、期待どおりに動作するために一般的に次の3つの手法を使用します。
通常の命令シーケンスから逸脱する分岐命令は、多くの場合ハザードを伴います。プロセッサが単一のタイムサイクルで分岐命令を実行できない限り、パイプラインは命令のフェッチを順次継続します。プログラマがプログラムの別の部分に制御を移しているため、このような分岐命令は実行できません。
条件分岐はさらに厄介です。プロセッサは、まだ実行されていない計算に応じて分岐する場合としない場合があります。さまざまなプロセッサは停止したり、分岐予測を試みたり、分岐が実行されるかされないかをそれぞれ想定して2つの異なるプログラムシーケンス(即時実行)の実行を開始したり、誤った推測に関連するすべての作業を破棄したりする可能性があります。[ a ]
分岐予測を適切に実装したプロセッサは、通常、分岐予測を正しく実行できるため、分岐によるパフォーマンス低下を最小限に抑えることができます。しかし、分岐予測が不正確な場合、プロセッサの処理負荷が増加する可能性があります。例えば、実行が開始された誤ったコードパスをパイプラインからフラッシュしてから、正しい場所で実行を再開するといった処理が必要になる場合があります。
パイプライン処理プロセッサ向けに書かれたプログラムは、速度低下を最小限に抑えるため、意図的に分岐を避けています。例えば、プログラマーは通常のケースを逐次実行で処理し、異常なケースを検出した場合にのみ分岐を実行します。gcovなどのプログラムを使用してコードカバレッジを分析することで、プログラマーは特定の分岐が実際にどのくらいの頻度で実行されるかを測定し、コードを最適化するための洞察を得ることができます。場合によっては、プログラマーは通常のケースと異常なケースの両方を分岐のないコードで処理できます。
右側には、フェッチ、デコード、実行、ライトバックの4つのステージからなる一般的なパイプラインが示されています。上部の灰色のボックスは実行待ちの命令リスト、下部の灰色のボックスは実行が完了した命令リスト、中央の白いボックスはパイプライン全体を表しています。
実行手順は以下のとおりです。


パイプライン処理を行うプロセッサは、パイプライン内で停止してバブルを生成することでハザードに対処することがあり、その結果、何も有用な処理が行われないサイクルが1つ以上発生する。
右の図では、サイクル3において、プロセッサは紫色の命令をデコードできません。これはおそらく、プロセッサがデコードが緑色の命令の実行結果に依存すると判断したためと考えられます。緑色の命令は予定通り実行ステージ、そして書き戻しステージへと進むことができますが、紫色の命令はフェッチステージで1サイクル停止します。サイクル3でフェッチされる予定だった青色の命令も、その後に続く赤色の命令と同様に、1サイクル停止します。
バブル(図中の青い楕円)のため、プロセッサのデコード回路はサイクル3の間アイドル状態になります。実行回路はサイクル4の間アイドル状態になり、ライトバック回路はサイクル5の間アイドル状態になります。
バブルがパイプラインから抜けると(サイクル6)、通常の実行が再開されます。しかし、すべてが1サイクル遅れています。色で示された4つの命令を完全に実行するには、7サイクルではなく8サイクル(サイクル1から8)かかります。[ b ]