- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理今ここ
- DB保存
前回、CPUが命令を取ってきて読み解いて実行することを見ました。今回は、その工程を重ねて同時に進める仕組みを扱います。
まず結論
パイプラインは、複数の命令の処理段階を重ねることで、単位時間あたりに完了する命令数(スループット)を増やす技術です。
1つの命令が始まってから終わるまでの時間(レイテンシ)は短くなりません。むしろ段の区切りにかかる費用のぶん、わずかに長くなります。ここを取り違えると、性能の話がすべてずれます。
5段のパイプラインを持つCPUで100個の命令を処理します。パイプラインが無い場合の500サイクルに対し、何サイクルで終わるでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
流れ作業にすると、待ち時間が消える
前回、CPUが「取ってくる・読み解く・実行する」を繰り返していることを見ました。ここで素朴な疑問が出ます。取ってくる作業をしている間、実行する回路は何をしているのでしょうか。
答えは「何もしていない」です。1命令ずつ順番に処理していると、どの瞬間も回路の大部分が遊んでいます。
そこで、工場の流れ作業と同じ発想を持ち込みます。最初の命令を「読み解く」段に送ったら、空いた「取ってくる」段で次の命令を取り始めるのです。こうすれば、どの段も常に何かしら仕事をしている状態になります。
これがパイプラインです。
洗濯物で考えると分かりやすい
洗濯を例にすると、この仕組みの本質と限界が同時に見えます。
【順番にやる場合】1山目: 洗う → 乾かす → たたむ(1〜3時間目)2山目: 洗う → 乾かす → たたむ(4〜6時間目)3山目: 洗う → 乾かす → たたむ(7〜9時間目)合計9時間【流れ作業にする場合】1時間目: 1山目を洗う2時間目: 1山目を乾かす / 2山目を洗う3時間目: 1山目をたたむ / 2山目を乾かす / 3山目を洗う4時間目: 2山目をたたむ / 3山目を乾かす5時間目: 3山目をたたむ合計5時間
9時間が5時間になりました。しかしここで決定的に重要な点があります。
1山目が仕上がる時間は、どちらも3時間で変わっていません。 短くなったのは「全部が終わるまでの合計時間」であって、「1山あたりの所要時間」ではないのです。
流れ作業にしたとき、「洗濯物1山が仕上がるまでの時間」はどうなりますか。
なぜ「ぴったり5倍」にならないのか
5段のパイプラインなら5倍速くなりそうですが、実際はそうなりません。計算してみます。
- ・一般式: k段でN命令なら N + k - 1 サイクル
- ・速度向上 = (N × k) ÷ (N + k - 1)。N が k より十分大きいときだけ k 倍に近づく
最初の命令が出てくるまでの立ち上がりの4サイクルが、どうしても余分にかかります。命令数が十分多ければこの差は誤差になりますが、短いループでは効いてきます。
さらに、実際にはもう2つの理由で理想には届きません。
- 段の区切りに費用がかかる — 段と段の間には、途中結果を保持する回路が必要です。この保持と受け渡しの時間が、各段に上乗せされます。
- 一番遅い段に合わせることになる — 5つの段のうち1つだけ時間がかかる段があると、全段がそれに合わせた刻みで動くことになり、速い段は待ち時間が生じます。
流れが止まる3つの事情(ハザード)
理想どおりに流れない事情は、3つに分類されています。
種類 何が起きるか 洗濯物で言うと構造ハザード 同じ回路を2つの命令が同時に使おうとする 乾燥機が1台しかないデータハザード 前の命令の結果がまだ出ていない 前の山が乾くまでたためない制御ハザード 分岐命令で、次にどこへ行くか分からない 次にどの山を洗うか決まっていない
このうちデータハザードは、多くの場合フォワーディングという工夫で回避できます。 計算結果をレジスタに書き戻すのを待たず、次の命令へ直接回してしまう仕組みです。ただし万能ではなく、メモリから読んだ値をすぐ使う場合などは、どうしても待ちが発生します。
そして制御ハザードが、次回のテーマである分岐予測につながります。 次にどこへ行くか分からないと、パイプラインに何を入れればいいか決まりません。かといって分かるまで止めていては、せっかくのパイプラインが台無しです。
「前の命令の計算結果を、次の命令がすぐ使いたい」という状況は、どのハザードですか。
CPUのパイプライン
5段のパイプラインで100命令が104サイクル。しかし1命令が始まってから終わるまでは、依然として5サイクルかかります。
100命令の合計 500 → 104サイクル (改善)1命令の所要時間 5 → 5サイクル (変わらず)※段の区切り費用で実際はわずかに増える
Webサーバのリクエスト処理
サーバを増やすと1秒あたりに捌けるリクエスト数は増えますが、混雑による待ちが無い状態なら、1リクエストの処理にかかる時間そのものは縮みません。むしろ振り分けの処理が加わります。
1秒あたりの処理数 100 → 500 req/s (改善)1件の応答時間 200 → 200 ms (変わらず)※LBを挟むぶんわずかに増える
CPUの内部とWebサーバの構成。層はまったく違いますが、同じ原理が働いています。それは何でしょうか。
「速くなった」と言うとき、何が速くなったのかを区別してください。
この章のまとめ
- パイプラインは、命令の処理段階を重ねて複数命令を同時に進める技術。
- 改善するのはスループット(単位時間あたりの完了数)。レイテンシ(1命令の所要時間)は短くならず、段の区切り費用でわずかに増える。
- k段でN命令なら N + k - 1 サイクル。速度向上は (N×k)/(N+k-1) で、ぴったりk倍にはならない。
- 流れを妨げる事情は構造・データ・制御の3ハザード。データハザードの多くはフォワーディングで回避できる。
- 段を深くすれば良いわけではない。Pentium 4は31段まで深めて方針転換した。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
パイプラインによって改善されるのは何ですか。
用語集(5語)
- パイプライン
- 命令の処理段階を工場の流れ作業のように重ね、複数の命令を同時並行で進める技術。スループットを上げる。
- スループット
- 単位時間あたりに完了する処理の数。パイプラインが改善するのはこちら。
- レイテンシ
- 1つの処理が始まってから終わるまでの時間。パイプラインでは短くならず、むしろわずかに長くなる。
- ハザード
- パイプラインを滞りなく流せなくする事情。構造(資源の衝突)・データ(結果待ち)・制御(分岐先が不明)の3種類がある。
- フォワーディング(バイパス)
- 前の命令の計算結果を、レジスタに書き戻すのを待たずに次の命令へ直接回す仕組み。多くのデータハザードによる停止を回避できる。