Memo

2010年2月28日

一昨日、Erlangのことを書いたので、そのErlangのこと。Erlangを初めて知ったのは当方が学部か修士学生だった頃でした。普通のコンピュータサイエンスの学生であればErlangを知ることはなかったと思いますが、所属した研究室で某通信機器メーカとの産学共同研究に関わることになりそのテーマが交換機(それもC-TRON)の制御のためのオブジェクト指向言語だったのです。当時の交換機はChillなどの専用言語を使っていたわけですが、それをオブジェクト指向風に書きたいというものでした。そのため欧州の大手の通信機器メーカEricssonが作った通信用記述言語としてErlangをたまたま知ったというのが実状。

ちなみに昨日、ErlangにはCSP的なガード記述があると書きましたが、これはErlangの歴史を考えれば当然。詳しくは設計者の一人がErlangの歴史に関する論文を書いているので、そちらをご覧いただければいいのですが、せっかくなので当時の状況などを少々。

通信プロトコルの開発では、OSI的な世界では、まず仕様をきっちり決める。きっちり決めると言っても自然言語で仕様を書くと解釈にブレが出るので、数学的に裏付けられた方法で定義をする。そうした定義方法のひとつが形式仕様記述言語。簡単に言えば数学的に定義されている言語を使ってプロトコルを定義するという方法。その形式仕様記述言語にはいろいろあったのですが(過去形)、そのなかにCSPにデータ構造を導入したような形式仕様記述言語Estelleがあって、そのEstelleにより記述したプロトコル仕様を実装するためにEricssonはConcurrent Pascalを作っていた。ErlangはそのEricsson版のConcurrent Pascalの影響を受けたというか、それを拡張して実装したはず。なんでこんな事情を知っているかというとEstelleを含む通信仕様記述言語のISO委員だから(Estelleの拡張版の規格化では多少の貢献があったりします、ちょっと自慢してみたり)。

まぁ、当方の話はどうでもいいのですが、ErlangがCSP的な記述ができるのは当然というか、Erlangの前身の言語がCSP的な記述の実装言語ということ。あとElrangは交換機用言語として設計されたこともあり、実行単位の粒度の違いは気をつけた方がいいかもね。交換機の実行モデル(そんなものがあったとはもえないが)は実行単位が小さい、つまりスレッド粒度が小さいので、通常ではスレッドに割り当てなかったような小さい処理もスレッドに割り当てた方が実装しやすいし、スループットもあがるのですが、これをサーバ環境で動かした場合に実用になるかは別問題。そうそう元交換機技術者の方がErlangはうまく使いこなせるかもしれませんね。ただ、Erlangという意味ではないですが、一般論として交換機用の言語は弱いところもあります。交換機の世界というのはエラーハンドリングを含めてハードウェア的に多くのことが解決される世界、TCP/IPのようになんでもソフトウェア的に解決しないと世界には向かないのです。Erlang好きの人に反論されるそうなので、ちょっと補足しておきますが、いまのErlangは交換機だけではないのでしょうが、癖がある言語である以上は記述対象がErlang向きか不向きかを見抜く眼力に必要だと思います。

まぁ、こんなわけでErlangというと20年前の古い言語という印象でしたから、最近になってErlangという名前を見聞きしても、当時のErlangとは違う言語としばらく思い込んでいたぐらい。なお、Erlangがこの時代になって流行る理由はいろいろあるのでしょうが、Erlangは交換機プログラムの記述として開発されたことを考えると、いまはTCP/IPなどの通信プリミティブが隠蔽されて、ある意味でいまのシステムは往年のチャネルベース通信プログラミングに近い世界になっているからかもしれません。実はErlangそのものよりも、Erlangがいま脚光を浴びる背景の方が断然興味深いです。

2010年2月27日

昨日の続きです。当時の並行オブジェクト指向で議論されていた話題に並行処理の定義方法があります。ちょっと歴史を説明しないといけないのですが、ひとつのプログラム内に並行処理をいれると効率的な処理ができる一方で、前述のように排他制御や同期処理をしないといけないのでプログラミングがたいへんになります。初期のUnixはそれを嫌って、各プロセスに割り当てるスレッドは高々一つと制限しました。そのうえ並行処理を行うときはプロセス自身を複製(fork)するという方法をとって、並行実行単位間の共有変数を排除。この結果としてはUnix向けプログラミングは簡単化させて、Unixの普及の要因の一つになったと思います。ここで興味深いのはUnixの前身のMulticsではプログラムの並行実行、つまりマルチスレッド実行を許していたので、Unixはプログラミングのシンプルさ・容易さのために、あえて低機能化させたといえます。

並行処理もif文やwhile文と同じ制御フローですから、これらの制御フローと同様に構文として記述すべきという考え方がありました。プログラムのブロック文に対して明示的に並行実行を制限する方法です。例えば二つのブロックは並行実行されるとかね。この場合はプログラマーがスレッドの起動を含めて定義することになり、きめ細かい制御はできますが、(モニターなどを導入するにしても)共有変数などのを管理するのは容易ではありません。

また、構文的な記述ですが、非明示的に並行処理を記述する方法もいくつか導入されました。例えばAdaなどはCSP的なガード付きコマンドという概念を導入して、スレッド実行可能な関数やサブルーチンに実行に必要な前提条件を定義しておいて、その前提条件が成立すると実行されるという方法をとりました。例えば通信データが届くと、所定のサブルーチンが呼び出されて実行するやり方。この方法ではスレッド起動そのものはシステム側に任せることになりますが、通信や制御のプログラムの場合はイベント駆動になることが多いのですが、この方法はイベント駆動的な処理では便利なので他の並行処理記述言語でも導入されました。最近でいうとGoもこれに近いプリミティブを導入しています。

ちょっと話題から外れますが、1990年前後に盛んに研究された並列Lispを含む並列関数型プログラミング言語では、関数単位にスレッドを割り当てました。これは関数の実行には副作用がすくないことを利用したわけですが、関数自体はどう呼び出されるかわからないので、並列度が上がりすぎるという問題を抱えていました。マルチコア時代は関数型言語の時代とかおっしゃる方は多いのですが、並列度を上げればいいというものでもなく、実際、スレッド数が多くなるとコンテキストスイッチのコストが大きくなります。それと歴史的に言えばプログラマーが並行実行を制御できない言語はベンチマークではよくても実システムでは向かないのですよね。だから最近話題になっている関数型言語による並列処理も同じ問題を抱えることになるでしょう。また、関数型言語だけではなく、手続き型の言語でも関数やサブルーチン呼び出し時に並行実行させるタイプもありました。もちろん、これら以外に並行処理の記述はいろんな方法がありますが、だんだんマニアックになっていくので、話を元に戻しましょう。

さて昨日は並行オブジェクト指向の話題だったので、話を並行オブジェクト指向に絞りますと、昨日書いたように当時の研究では複数オブジェクトは並行実行させるが、各オブジェクト内部は高々シングルスレッドとすることで、オブジェクト内部は逐次処理、オブジェクト間の相互作用として並行処理をさせる方法と、構文として並行処理を導入する方法が議論されていました。しかし、1995年頃に登場したJavaはスレッドそのものをオブジェクトとして扱う、つまりスレッド実行をオブジェクトとして導入して、そのオブジェクトに処理を渡すと並行処理されるという方法を導入しており、そのJavaの普及とともに並行処理の記述方法の議論は下火になってしまいました。昨日の話題にあげたScalaやErlangは結局、時計の針を戻すというか、Java登場以前にもどった感じなのですよね。

脱線しますが、Javaの並行記述は中途半端でしたね。スレッドそのものをオブジェクトとしたので、Java言語自体には並行記述と独立させることに成功したのに(ここまではよかった)、同期制御(synchronized)を言語側に導入してしまったので、並行実行はオブジェクトとして定義して、一方で並行実行の同期制御は言語として定義するということになっています。インターフェースに類似した方法で、クラス定義そのものとは別に同期制御を宣言できるようにしておくべきだったかもしれません。というのはクラス定義ではあくまでも機能要件を書くべきであり、同期などの非機能要件はクラス定義に埋め込まない方がいいので。

いずれにしても並行処理はプログラミングにおいては鬼門であり、多くのプログラミング言語は並行処理は避けていました。というのは並行処理というのはプログラミング言語では扱いにくいのです。つまり並行処理は実行に依存するのでプログラミング言語だけで定義できる世界ではなく、並行実行を表す計算モデルも考えないといけないのです。この並行計算モデルもたくさんあります(実は博士論文は並行計算モデルだったりします)。ScalaやErlangが流行るところをみると、CSPやActorモデルなどが再び脚光を浴びるのかもしれませんが、並行計算モデルとプログラミング言語の実装・実行とはギャップがあるので、並行計算モデルがあれば相互排除や同期の問題が解決するというものでもないのですよね。

2010年2月26日

コンピュータサイエンスの技術はある方向に振れると、しばらくすると逆の方向に振られることがあり、それを繰り返します。例えばいまはクラウドコンピューティングが流行っていますが、10年前はPeer-to-peerシステムが流行っていました。クラウドコンピューティングは多数のサーバで処理をさせるので分散システムの一種にもみえますが、サーバを一カ所に集めるという点では集中システムとしていいでしょう。すくなくてもPeer-to-peerシステムとくらべたら十分に集中システムですから。

最近、興味深いのはオブジェクト指向言語のScalaやErlangが話題を集めていることでしょうか。どちらもActor Modelをベースにしているそうですが、オブジェクト指向言語の歴史でいうと、Actor Modelなどの並行処理用オブジェクト指向言語の研究が盛んになったのは1985年からの6,7年ぐらいだと思います(Actor Model自身はもっと古いですが)。そして1990年後ぐらいから議論されてきた話題のひとつは、各オブジェクトは高々のひとつの実行スレッドとするシングルスレッド実行モデルと、一つのオブジェクトでも複数同時処理を許すマルチスレッド実行モデルのどちらがいいかというもの。

さてシングルスレッド実行モデルとマルチスレッド実行モデルは何が違うかというと、前者は各オブジェクトは能動的に処理してもよい、つまり各オブジェクトに高々一つのスレッドを割り当てるが、オブジェクト内部的には逐次プログラムですから、オブジェクト内部で排他実行や同期をしなくてもいいので、オブジェクトのプログラミングは楽になります。一方、マルチスレッド実行モデルでは各オブジェクトは能動的に処理されますし、各オブジェクトにも複数のスレッドが割り当てられます。このため処理効率はあがりますが、オブジェクト内部で排他実行や同期する必要が出てきます。どちらもメリットとデメリットがあるのですが、シングルスレッド実行モデルの代表格というのが前述のActor Modelでした。

ScalaやErlangなどの並行処理の記述性を重視した言語が、シングルスレッドモデルというか、Actor Modelを採用したのは当然の帰結なのですが、正直言って、1990年後の議論がリバイバルを見るような感じですね。(Erlangは試しで使った程度ですが)Scalaは実装につかったりします。ただ、新しいプログラミング言語というよりも、昔のプログラミング言語で書いているという感覚はぬぐえないのですよね。ScalaやErlangに関しては、しばらくして実装事例が増えるとともに、記述の容易さよりも性能を重視して、マルチスレッド実行モデルがもてはやされるのでしょうね。歴史は繰り返されますから。もちろん当方の予測を裏切るような展開を期待しておりますが。

むかしの話ですが、4年生になって研究室に配属されて初めて研究室にいったときに、最初にあった人が、たまたま通っていた大学に転がり込んでいたCarl Hewitt(Actor Modelの創始者)さんでした。まぁ、このあたりから人生が狂ったような気もしますが、いま考えるとすごい環境でしたね。ちなみに4年生の時に初めて電話で話した外人研究者はL.Lamportさんでした。大学で講演する予定があり、その日程をきくために電話をかけてきたようでした。英語がさっぱりわからず別の人にかわってもらいましたが。

佐藤一郎: Web日記 (2010年) (via kuenishi) Via amnesiac
  1. kanbe reblogged this from kuenishi
  2. iyoupapa reblogged this from kuenishi
  3. siritori reblogged this from kuenishi
  4. kuenishi posted this
To Tumblr, Love PixelUnion