つきいち日記へ戻る  

 CPUと並列性

2003年6月7日 22:16:14

 単なるエッセーである。

 今はほとんど言わなくなったが、RISC/CISC論争というのがあった。CISCはComplex Instruction Set Computerのことで、具体的にはIBMのメインフレームS360や今は無きDECのスーパーミニコンVAX、そしてIntelのPentium等のCPUを指す言葉である。私も8086のころはアセンブラも良く書いたものだが、たしかに高級言語向きと思えるアドレス指定命令などがあった。IBM 360はFORTRAN/COBOLの動作の最適化を目指していたし、VAXもFORTRAN向きのアーキテクチャだったそうである。
 もともと機械語はあとでアップデートプログラムでパッチを当てる、などという「後出し」が効かなかったので、初期にはハードの節約のために簡単な動作をさせていた。伝説によると、IBM 360に至ってもハッカーなぞはコンソールのスイッチで機械語を直接書き換えてデバッグしたらしく、そうなると、この世のどこにもソースプログラムの無いソフトができてしまうわけである。
 さて、計算機はどうせ大半がFORTRAN等の高級言語で書かれたプログラムを実行するのだから、FORTRAN等の言語構造を生かそう、ということらしい。その結果出てくる複雑な機械語命令はマイクロプログラムと言ってCPU内の小さなプログラムでより簡素な内部命令群に分解して実行した。機械語の意味が豊富になっていった訳である。

 RISC (Reduced Instruction Set Computer)陣営の言い分は、マイクロプログラムはCPUを複雑化させるので高速化できない、それよりも命令を簡単にしてコンパイラにがんばってもらい、CPUは簡素に保つのが良い、少なくとも最初はそのように宣伝していたと思う。
 RISCの方が命令が簡単だから単位時間当たりの実行数も多くなり、数値上はRISC陣営の圧倒的勝利に見えた。
 その後の歴史はどうなったか。VAXはAlphaというRISCに移行した、が悲しいことに絶滅寸前。IBMはCISCのまま、IntelもCISCのままである。本家RISCのSun SPARCはサーバとしてはまずまず、元祖RISCのIBM PowerはMacや企業の基幹システムで使われている。なおWindows XPはVAXの専用OSであったVMSの子孫である。
 この争いの過程で注入されたCPUの改良技術は想像を絶するものだったであろう。当事者はたまったものではなかったと思うが、傍から見ていると、単位時間当たりのCPUブロックと主記憶間の情報交換量で処理能力は決まってしまう、との仮説を立てざるを得ない。

● CP (Central Processor)

 あまり一般的な用語ではないが、私が衝撃を受けた言葉にCPがある。これはIBMのMVSと呼ばれる大型機のOSの独特の用語で、CPU (Central Processing Unit)と同じものである。CPは、しかし、マルチプロセッサシステムの中の一つのCPUを指すための言葉である。通常は単に(個々の)プロセッサと呼ばれる。
 なにが面白いかというと、CPはOSが扱う資源、つまりメモリや入出力装置と同じレベルに位置づけされるのである。CPUといえばコンピュータの心臓部であるのだが、OSというプログラムから見れば自分が使える機能の一つに過ぎない。
 内情を知らない人に誤解を与えてもいけないので種明かししておくと、管理プログラムは、通常、最初のCPUで実行されていて、時分割された自分自身や他のCPUを実際の仕事に割り当てるのである。

 私はCPがあまりに便利な用語なので、いまでも時々使用する。というのも、「CPU」では一国一城の主のようでマルチの感じがしないし、「プロセッサ」ではOSというプログラムから見た感じではなく、ICチップを想起してしまうからだ。OSから見たときに仕事をしてくれる実働部隊がCPなのである。「OSから見たときに」、という表現は奇異に思える方もいるだろうが、ウィンドウシステムのデスクトップにCPUアイコンがいくつか見える感じ、といえば感覚が分かると思う。

 専門用語の嵐になって恐縮だが、話を続けるためにはしかたがない。CPは最初は別々に用意された仮想記憶空間で働いていた。もうすこし正確に言うと、ジョブとかプロセスとか呼ばれる一単位の仕事に一つのCPUが専念していたのである。こうすれば同じメモリの場所に違う情報を同時に書き込むことは絶対に起こらない。しかし、今の主流はプロセスではなくスレッドと呼ばれるプログラムの流れの単位ごとにCPが割り当てられる。同じ仮想空間内で複数のCPが働いているのである。もちろん、CP同士が競合しては困るので、それなりのプログラム作法が必要である。

 歴史的にはマルチプロセスはジョブの空き時間がもったいないから、という理由と、多人数で一つのコンピュータを使うタイムシェアリングシステム(今となっては懐かしい言葉である)で互いに干渉せずに仕事をしたいから、の2つの理由で開発が進んだと記憶している。
 しかし、今ではCPがたくさん使えるので、一台の機械のパフォーマンスを少しでも上げるために利用されている、という感じがする。つまり、並列度を向上させるための一つの手段、と考えることができる。

● HT (HyperThreading)

 案の定、一つのCPUチップに複数のCPを乗せようという計画が出てきたようである。Pentium 4のHyperThreadingには驚いた。というのも、何となくぱっとしなかったPen4なのだが、あっと驚く仕掛けが隠されていたわけだ。RISC陣営のSun SPARCはもっと大規模なスレッド並列を狙っているようである。

 さてこうなってくると、少々心配なのは、マルチスレッドがハイパースレッド向きか否か、ということである。私もソースプログラムの流れを簡単にするためにマルチスレッドプログラミングすることがある。しかし、主目的はマルチCPU機で動作させたときに高速になれば、ではなく、あくまで書きやすさである。
 またまた誤解を与えてはいけないので解説を加えておくと、マルチスレッドにすると複数のスレッドが干渉しないように、余分なソースコードが必要になるから、複雑化するのではないか、という意見が当然出てくるであろう。もちろん、複雑化が目的のマルチスレッドはできることはできる(^_^;)。
 しかし、たとえば、多重ループの内部にアルゴリズム上直接は関係のないコードを埋め込むのは、結構気を使って骨の折れる仕事であったりする。ここをマルチスレッドの威力で、スレッド同士の情報交換に置き換えてしまうわけだ。結果をメモリに書き込んだから、どうぞそちらで利用してくださいね、ということである。

● KL1

 で突然ではあるが、想起されるのが第五世代コンピュータ計画の並列推論エンジン向けの言語、KL1である。
 KL1の母体はPrologであるから、Prologが使いこなせていないと、以下の文章はほとんどジャーゴンであろう。不幸なことに、Prologの実用技は、なぜか、広まらなかったのである。「データベース」にassertしたときに変数に値があるか否かでどのような動作をするか、末尾再帰の最適化によるループ構造、差分リストによる高速データ連結、この辺りはめちゃくちゃ重要なのだが、LISPと異なり、実用のための良書がほとんど見られなかった。実用Prolog処理系の動作のくせが人工知能の流行中は収束しなかったのも不運であった。

 KL1はPrologが逐次計算機、つまりCPUが一個の計算機向けなのと異なり、マルチCPU向けの言語である。
 Prologと非常に似た書き方をするのだが、パターンマッチングと呼ばれるデータベース検索部分が複数同時に起動される。そして、時間的に最初にマッチした節(述語 = プログラム、の一部)が他の節を停止させてしまう(注: コミットのこと)。これがいわゆるグリーンカット、つまり一意性が確保されていれば問題がないのだが、そうでない場合(レッドカット)、時の運によって選ばれる動作が異なることがある、ということで、KL1はすさまじい議論を引き起こしたのであった。

 まったくややこしい話であるが、Prologの差分リストがKL1ではストリーム、つまりデータの流れになっており、プログラムの単位である述語(普通の言語の関数のこと。真偽値を返すから述語と呼ばれる)はストリームを受け取り、処理結果のストリームを吐き出すことになっている。入力ストリームも出力ストリームも1本とは限らないのがミソで、ストリームのネットワークが組める。
 UNIXに詳しい方なら、UNIXのC言語コンパイラが処理プログラムをストリームでつないで、まるで流れ作業していたのを思い出すと思う。UNIXのプロセスも入出力ストリームをそれぞれ複数持つことができる。

● スレッドとストリーム

 プロセス間の通信ではストリームはパイプとして知られており、特に名前付きパイプはリモート・プロシジャ・コールと絡んでいて興味深いのだが、これもなぜか、ウィンドウシステムが流行ったとたんに沈黙状態になってしまった。私などはWindows NTに名前付きパイプがあることを知って、これで新しい時代が来ると、ぬか喜びしたものである。まあ、(多分)対抗馬であったDCOMとかDSOMとかの批判はしないでおこう。でも、一時期流行った複合ドキュメントとか今頃はどうなっているのかとちょっと心配ではある。

 CPUの時分割はスレッド単位である。スレッド間の通信は主として主記憶内の静的データで行われている、と思う。プロセス間通信用語で言えば共有メモリである。スレッドはもともと仮想空間を共有しているのだから、当然であろう。
 だからスレッド間通信をストリームでやるなどと言ったら、現時点では変人扱いされかねない。

 だが、もし、現状を突破する大規模な並列化技術があるとしたら、この辺りにヒントがあるのではないかと私は考えている。
 スレッドの起動は関数の起動として記述される。この時、パイプに相当するデータ型(オブジェクト)が利用でき、CPUが直接でも間接でも支援してくれたら、と考えるのである。