TT Lab
はじめる
学ぶ 学習パス コース

コンピュータ構成

命令サイクル — 取り出し、解読、実行

TT Labで続きを見る

一言でいうと

CPUは、メモリから命令を1つ取り出して(フェッチ)、意味を解読し(デコード)、実際に実行する(実行)という作業を、延々と繰り返す機械です。

なぜ必要なのか

プログラムは、結局のところメモリに並べた数字の塊です。この数字を誰がどんな順序で読んで、何をするかを決めなければなりません。初期の設計者たちが下した決定が今まで続いていて、核心は2つです。1つ目は、命令とデータを同じメモリに置くことです(ノイマン型アーキテクチャ)。2つ目は、次に実行する命令のアドレスを、1つのレジスターに持たせておくことです(プログラムカウンター)。

この2つの決定のおかげで、CPUは非常に単純な規則を1つだけ守ればよいことになります。「プログラムカウンターが指す場所から命令を取り出して実行し、カウンターを次へ進める。」ループも関数呼び出しも条件分岐も、すべてこのカウンターの値を変えることにすぎません。

どう動くのか

1サイクルは、おおよそ次のように流れます。

  1. フェッチ(Fetch): プログラムカウンターが指すアドレスから命令を読み、命令レジスターに入れます。
  2. デコード(Decode): ビットパターンを見て、どの演算か、オペランドがどこにあるかを判断します。
  3. 実行(Execute): 算術論理装置が計算したり、メモリにアクセスしたり、カウンターを別の場所へ移したりします。

問題は、これを順番にだけ行うと、ほとんどの回路が遊んでしまうことです。デコーダーが働いている間、フェッチ側は休んでいます。そこで登場したのがパイプラインです。洗濯機と乾燥機を分けて使うように、1番目の命令がデコードされるときに、2番目の命令を先にフェッチします。段階が5つなら、理論上、スループットが5倍になります。

タダではありません。パイプラインは、3つのハザード(hazard)を生みます。

ハザード 状況 対応
構造ハザード 2つの段階が同じ回路を同時に使いたがる リソースを分けるか、片方を遅らせる
データハザード 前の命令の結果を、後ろの命令が必要とする フォワーディング、それでもだめならパイプラインを停止
制御ハザード 分岐の結果がまだわからない 分岐予測、外れたらパイプラインを空にする

3つ目が特に高くつきます。分岐予測が外れると、すでに進行中だった命令をすべて捨てなければなりません。現代のCPUのパイプラインは15段を超えることもあるので、外れるたびに数十サイクルが失われます。ソート済みの配列を回すループが、ソートされていない配列より数倍速いという有名な現象は、ここから生まれます。条件が規則的なら、予測器が当てるからです。

現場での姿

性能測定をするとき、よく目にする指標がIPC(サイクルあたりの命令数)です。クロックが同じでも、IPCが2倍なら2倍速いです。逆に、IPCが0.3のように低く出るなら、CPUが計算できないからではなく、何かを待っているというシグナルです。たいていはメモリです。

そのため、「CPU使用率100パーセント」をそのまま「演算ユニットが100パーセント計算中」と読んではいけません。コアは非アイドル状態でも、キャッシュミスのようにメモリ階層を待って、実行が停止することがあります。ただし、ブロッキングI/Oで眠ったタスクの時間は、通常そのプロセスのCPU使用時間には数えないので、メモリのレイテンシとI/O待ちを区別する必要があります。

続くクイズで確認すること

パイプラインが、なぜスループットを増やしながら命令1つのレイテンシは減らせないのか、分岐予測の失敗がなぜ高くつくのかを、自分で説明できるかを点検します。