第7回

第7回: マイクロプロセッサ

制御方式(p.58)

いままで加算器や乗算器などの演算回路について見てきましたが、 「コンピュータ」は、これらの演算回路を、意図したとおりの順序で 動かす必要があります。 この「意図した順序」とは、いわゆるプログラムのことであるわけですが、 このプログラムの回路としての実現方法には、大きく分けて 2通りがあります。

1つは、ハードワイヤ方式とと呼ばれるもので、 この手順を論理回路として作りこんでしまうものです。 皆さんが既に知っている、いわゆる「順序回路」は、 状態遷移表・状態遷移図に従って、内部の状態、およびそれにあわせて 出力が、設計した手順の通りに変わっていく、という回路ですが、 これが、まさにハードワイヤ方式の制御回路ということができます。

もう1つは、プログラム制御方式と呼ばれるものです。 これは、処理手順を、メモリに「命令」からなる「プログラム」として 格納しておき、そこに書いてある命令にしたがって、 処理を進める、という方式です。 一般的には、現在実行しようとしている命令が入っているメモリのアドレスを 示す「プログラムカウンタ(Program Counter; PC)」と呼ばれる 変数の内部メモリ(レジスタ)をもち、 メモリの中のPCが示すアドレスから命令を読み込み(フェッチ(fetch))、 それを解読(デコード(decode))し、その結果にしたがって 演算器やデータメモリへのアクセスを実行する、という手順を 繰り返すことになります。

ハードワイヤ方式は、回路構成が単純な分、高速動作が可能で 回路規模も小さくなりますが、処理内容が順序回路として 作りこまれているわけですので、処理内容の変更は、基本的にできません。 一方、プログラム制御方式は、回路が複雑になる分、処理速度は 遅くなりますが、処理の変更の柔軟性が非常に高くなるという 利点があります。

パイプライン処理(p.58)

特にプログラム制御処理において、処理速度を上げる方式の1つが、 パイプライン処理です。 パイプライン処理は、処理を構成する処理ステップを、 流れ作業のように順番に、かつ同時に行う方式です。 例えば、先ほどのプログラム制御方式におけるプログラムの実行手順のうち、 命令の読み出し(fetch; F)と解読(decode; D)、実行(execution; E)は 順番に行われますが、次のように同時に行うことができます。
---------------> t
 F1 D1 E1       : 命令1
    F2 D2 E2    : 命令2
       F3 D3 E3 : 命令3
このようなパイプライン処理の各ステップを パイプラインのステージ(stage)と呼びますが、 処理のステージへの分割を、同時にできる部分を選ぶように 工夫することで、1つのステージの実行に要する時間を 短くすることができ、結果として、全体の処理速度を 向上させることができます。 次の図は、A×B+Cを求める積和演算(multiply and accumulate: MAC)回路を パイプライン処理によって高速化し、ゲート長90nmのMOSトランジスタを 使ったCMOSによって5GHzの動作を実現した例です。
(「VLSI工学-基礎・設計編-」(岩田、コロナ社)p.60より)
パイプライン処理は、処理のステージを細分化し、 同時に行うことができる部分を並列に行うことによって 全体の高速化を図るものですが、 その代償として、結果が出てくるまでに、ステージの数の分だけ 時間がかかる、ともいえます。 これは、工場での流れ作業でも同じですが、 最初の製品が出てくるまでは時間がかかっても、 それからあとは、1つのステージ分の時間ごとに 1つずつ製品がでてくるため、そこから先は 非常に高速になっているように見える、という言い方もできます。 この、最初の処理が一通り終わるまでの時間を レイテンシ(latency)と呼びますが、 これはパイプライン処理においては避けられないものです。

このレイテンシが深刻になるのは、プログラム中に処理の分岐がある場合で、 例えば先ほどのパイプライン処理の例で、命令1が分岐命令だったとすると、 続く命令2、命令3は、実は実行されない命令であるために捨ててしまい、 分岐先の命令の読み出しから再度、はじめなくてはいけません。 このように、順調に流れ作業が進んでいたものが、 分岐命令によってパイプライン処理をやりなおす状態を パイプライン処理の乱れ、と呼びますが、 これは根本的には避けられない問題です。 その対策として、分岐命令がある場合には、分岐先を予測しておいて 読み出しを先に行ったり(予測)、実行自体を先に初めてしまう(投機的実行)、 という対策などがあります。

MPUのアーキテクチャ(p.62)

先にも述べたように、プログラム制御方式は、プログラム中に書いてある命令から なるプログラムを、その手順どおりに実行する、という方式です。 これは、いわゆるNeumann型コンピュータそのものです。 このNeumann型コンピュータを、集積回路として実現したものを マイクロプロセッサ(Micro Processing Unit; MPU)と呼びますが、 それは一般的に次の図のような構成をとります。
(「VLSI工学-基礎・設計編-」(岩田、コロナ社)p.62より)
MPUの動作は、基本的に、命令の読み出し→解読→実行、の繰り返しですが、 この「実行」の部分では、演算対象のデータをメモリから読み出したり 結果をメモリに書き込む、という動作を含むことが多く、 結果として、メモリへの命令・データの読み書きを同時に行うことが できない部分が、全体の処理速度の向上の制限要因となることが 知られています(Neumannボトルネック)。 この対策の1つとして、命令用のメモリとデータ用のメモリを分けておき、 それぞれのアドレスバス・データバスも別々にもつことで、 命令読み出しとデータ読み書きを同時に行うことができるようにする 構成(Harvard Architecture)が知られています。 (ちなみに命令とデータでメモリを共有する構成をPrinceton Architectureと 呼ぶ)

命令セット

一般に、MPUが、プログラムの実行に要する時間は、 「処理時間 = 命令数 × CPI × サイクルタイム」で求められます。 ここで、命令数とはプログラムを構成する命令数、 CPI (Clock per Instruction)とは1つの命令を実行するのに要するクロック数、 サイクルタイムとはクロック周期、すなわち1つの処理ステップ(パイプラインの ステージ)を実行するのに要する時間、です。

MPUの高速化のために、サイクルタイムを短くするのが手っ取り早いのは 事実ですが、それ以外のどちらを優先して小さくするか、という方針が、 それぞれ存在します。

1つは、命令数を少なくしようという方針です。 これは、MPUが使うことができる命令の種類を増やし、 ある程度複雑な処理も、1つの「命令」として記述できるように することで、プログラム中の命令数を減らそうというものです。 この方針はCISC (Complex Instruction Set Computer)と呼ばれます。 これは、本来は、まだ半導体メモリが高価で低速であったころに、 メモリの容量を減らし、またメモリへの読み書きの頻度を減らすために 考えられた方針です。 CISCでは、1つの命令を構成するバイト数が短いもの(単純命令)と 長いもの(複雑命令)がプログラム中に混在するため、 パイプライン処理を効率化しにくく、また読み出した命令を 解読する命令デコーダとそれに伴う制御回路が複雑化するため 高速化が難しくなる、という問題点があります。

もう1つは、CPIを減らそうという方針です。 これは、MPUがもつ命令の種類を減らすことで、 命令デコーダとそれに伴う制御回路を単純化し、 ほとんどの命令を1つのクロック周期で実行を完了することが できるようにするものです。 この方針はRISC (Reduced Instruction Set Computer)と呼ばれます。 これは、当然の結果としてプログラムを格納するのに必要なメモリ容量が 増加し、またメモリのアクセスの頻度も増加します。 しかし前者に対しては半導体技術の進歩による解決を、 後者に対してはキャッシュメモリを用いるメモリの階層構造によって 解決を図ろうとするものです。 また命令数が少ないために命令を構成するバイト数が固定され(固定長命令)、 パイプライン処理が乱れにくいという利点もあります。

CISCとRISC、どちらが優れているか、という論争・競争が行われた時代も ありましたが、現在では、両者の長所をとりいれた、CRISCとでも呼ぶべき 構成が一般的になっています。 すなわち、多くの命令は固定長・1クロック実行とし、 それ以外に可変長命令も備え、また大容量のキャッシュメモリを 備え、各種の方策によってパイプライン処理を乱れにくくする工夫を とりいれたMPUが一般的になっています。 これは、スケーリングをはじめとする集積回路技術の進歩によって 現実のものとなったものです。

スーパースカラ・VLIW

MPUの高速化の別の方針は、並列処理を行おうとするものです。 これには、大きく2種類の方針があります。

1つは、プログラム中の命令を複数同時に読み出し、 依存関係がなくて同時に実行しても差し支えないものを 同時に実行する、というもので、 スーパースカラ(super scaler)と呼ばれます。 この方式では、命令デコーダや演算器も、必要に応じて複数もつ 必要があります。 また、多めに命令を読み出してためておき、その中から、 先に実行できるものから先に実行してしまう (out-of-order実行)ような制御を組む方式もあります。

もう1つは、プログラムに格納する命令の1つで、 複数の演算器などを同時に扱えるようにしておき、 プログラムを生成する(コンパイルする)段階で、 使える演算器をできるだけ効率的に使えるように 実行順序などを最適化する方式で、 一般に1つの命令の長さが長くなるので、 VLIW (Very Long Instruction Word)方式と呼ばれます。 これは、いわば静的な並列化、ということができます。

このほか、MPUを含むコンピュータシステムの多くは、 WindowsやLinuxなどのOS上で動作しますが、 それらのOS上で動作するプログラムが、スレッドと呼ばれる単位で 時分割によって処理されます。 言い換えれば、複数のスレッドを、数msという短い時間で切り替えながら 順番に実行するわけですが、このスレッドの切り替えには MPU内のレジスタの退避などのオーバーヘッドが大きいため、 スレッド単位で並列処理できるように処理回路を並列化する マルチスレッディングや、MPU全体を、1つの集積回路内に 入れてしまうマルチプロセッサと呼ばれる構成も、 最近の集積回路技術の進歩によって現実のものとなってきています。

メモリアーキテクチャ

Neumann型コンピュータでは、メモリへの命令・データの読み書き(アクセス)は 避けることができません。 ところが、近年の集積回路技術の進歩により、 MPUの動作サイクルの短くなる度合いと、 メモリの応答速度サイクルの短くなる度合いが異なり、 両者の差が大きくなる一方となるという問題が顕在化してきています。 (下図)
(「VLSI工学-基礎・設計編-」(岩田、コロナ社)p.66より)
メモリのうちの、容量で大部分を占める主記憶装置には、 DRAM (Dynamic RAM)と呼ばれるもの(第9回で触れます)が用いられますが、 特にDRAMへのアクセス方式が、徐々に改良されて高速アクセスが 可能なものが実用化されていますが、なかなかMPUの高速化に追いつけない、 という実情がみてとれます。

これに対する対策の1つが、いわゆるメモリの階層化で、 小容量だが高速なメモリと、大容量で低速なメモリを併用することで、 平均的なメモリアクセス速度を向上させるものです。 小容量だが高速なメモリをキャッシュ(cache)と呼びますが、 このキャッシュも、速度と容量に応じて、1次キャッシュ、 2次キャッシュ、3次キャッシュ・・・と階層化するのが 最近は一般的です。 最近は2次キャッシュでも数MB、3次キャッシュで数十MBをもつものも 現実的になってきました。


配布資料(第7回) (PDF形式)
戻る