分散オペレーティングシステム特論レポート

600P031-4 高木祐介

2000-01-16

もっと強力で分かりやすいプログラミング環境を。エディタやコンパイラや実行環境を含むプログラミ

ング環境が理想的であれば、プログラマの需給バランスの問題は解決できると思います。

例えば、 Borland の Turbo シリーズは PC ユーザに広く使われました。また最近、 GUI のプログラミング環境が多く出回っています。これらのプログラミング環境は理想的とは言えませんが、アクセスしやすいのが売りです。

インタプリタはコンパイラよりも生産性が高いと言われています。また、 Lisp や Prolog などの記号処理言語、 Smalltalk などのオブジェクト指向言語、 Forth などの特殊な言語はプログラミング環境と共に提供され、熱狂的なファンを持ちます。

参考文献

1. Seif Haridi, Peter Van Roy, Per Brand, Chiristian Schulte, Programming Languages for Distributed Applications. Invited paper for New Generation Computing, 1998.

2. http://www.ueda.info.waseda.ac.jp/~takagi/

1 強力さと分かりやすさ

RPC、 X Window System、分散オブジェクト環境、 Jini などの分散フレームワークは、ネットワーク透明であることを理想として発展してきました。しかし、実際の分散フレームワークは大なり小なり分散性の明示制御を行ないます。\underline{透明性}は、プログラムの記述を容易にするため。\underline{明示制御}は、効率の良いプログラムの記述を可能にするため。この 2 つの流れは一見相反するように見えます。

しかし、文献 [1] によると、「分散システムはネットワーク透明でありながら、ネットワーク感知できるべき」です。プログラムの論理的な意味は、物理的な制約には捕らわれないはずです。

例えば、暗号化（平文を暗号へ変換する関数）は、どのホストで実行されても、紙の上で計算されても同じ結果が出ます。しかし、次のような物理的制約があります。

• 暗号を得たいのはローカルホストである。

• 平文はネットワークを流れてはならない。

これらの制約から、暗号化はローカルホストで実行されなければなりません。ネットワークが信頼できないから暗号化を行なうのに、平文がネットワークで伝えられるのでは意味がないからです。

論理的なアルゴリズムは、物理的制約を意識せずに記述することができます。ネットワーク透明であるとはそういうことです。その中で論理的に独立な並行プロセス群を切り分け、並列にあるいは分散して実行させるのが並行プログラミングです。ネットワーク感知できるとは、物理的制約を満たすために並行プロセスの分散実行を制御することができることです。

\underline{論理的意味}と\underline{物理的制約の解決}は、意味論 (semantics) と語用論 (pragmatics) に相当します。すなわち、分散プログラミングの容易さと効率の高さは、良いプログラミング・インタフェイスによって両立し得ると思います。

2 強力な柔軟性

極論を言えば、チューリングマシンに等価なプログラミング言語によって計算可能関数は記述できるし、マシン入出力を直接操作することによって分散／並列のための実行制御はできます。

しかし、もはやマシン語プログラミングはあまり行なわれません。計算資源のコストが 0 に近づき、プログラムの実行時間よりもプログラミングの時間の方が貴重になったからです。このような時代に、どれほど強力な柔軟性が必要でしょうか。

XML は SGML の柔軟性を制限したものと言えます。例えば、 XML ではタグを省略できません。 SGML のタグの省略は構文糖 (syntax sugar) の一種です。

Algol 60 の手続きには名前呼び (call by name) の規則があり、それを利用して GPS (General Problem Solver) 手続きを書くことができました。これは計算可能性と言う点では面白いものですが、 GPS という仮想計算機を書くようなものです。 GPS は非常に強力な手続きですが、実際に行なわれる計算の性質を決めるのは GPS に渡される引数の方です。

GPS のように強力な手続きを単純に記述できるほどの柔軟性は必要なのでしょうか。むしろ、 GPS の個々の呼び出しによって行なわれる計算を単純に記述できることこそ望ましいでしょう。その証拠に Algol 60 の子孫は名前呼びを行ないません。

3 論理構造の上に物理構造

論理的なアルゴリズムに対して物理的な実行方法を指定するには、例えば C の#pragma に始まる前処理命令があります。これらのプラグマは、プログラムに対してメタレベルで付加されます。つまり、プログラムの論理構造の上にプラグマによる物理構造を築くわけです。論理構造を無視してプラグマを指定しても意味がありません。例えば、プラグマを識別子の中に埋め込むことはできません。

論理構造の上に物理構造を築くというのは、計算機科学の常識と逆のように思えます。普通は、物理層の上に論理層が構成されているからです。例えば、 OSI 参照モデル、 Ethernet 上の TCP/IP、 CPU 上で働く OS などです。

しかし、 OS 上で仮想計算機を提供する C や Java、コンパイルスイッチ、 C++の inline 宣言、宣言型言語の入出力などは論理構造の上に物理構造を指定していると言えます。また、 Java の配列、 CPU のパイプライン／投機計算、コンパイラのレジスタ割り当て／ループ展開／計算の簡約化などは、物理構造を指定するまでもなく論理構造から最適な物理構造が分かる例です。すなわち、物理的な実行制御のインタフェイスが必要なのは、次の 2 つの条件が両方とも満たされている場合です。

• 物理的な実行方法が複数通り存在する。

• 論理的意味から最適な実行方法を一意に決められるほどには、物理的制約が分かっていない。

4 拡張可能性

現在のさまざまなフレームワークは、拡張性を重視しています。例えば、オブジェクト指向では拡張のためにクラスを継承してサブクラスを定義します。

このような拡張はクラスツリーの全貌を把握している人にとっては簡単ですが、そうでない人にとってはほとんど不可能です。例えば、 Java 標準 API の拡張はアプリケーションプログラマには難しいことです。このため、拡張前のシステムを完全に理解せずに拡張を行なうという研究があります。しかし、理解せずに拡張を行なうことが可能であるべきでしょうか。理解せずに拡張したいのではなく、本当は理解して拡張を行ないたいのだが、システムのあらゆる領域を理解するのは現実的でないので、そこまで理解しなくても拡張可能なインタフェイスが望まれていると言えます。文献 [1] では、分散アプリケーションの問題領域が

次の 5 つに切り分けられています。

• アプリケーション機能

• 分散構造

• オープン・コンピューティング

• セキュリティ

• フォールト・トレランス

アプリケーション機能は、そのアプリケーションの論理的意味です。分散構造は、プロセスを物理的にどう分散させるかです。残りの 3 つの問題領域は、プログラミング・インタフェイスによって支援されるべきでしょう。

それ以外にも重要な問題領域としてリアルタイムがあります。分散構造、フォールト・トレランス、リアルタイムの各領域は論理構造の上に構築できるべきでしょう。なぜなら、これらの領域を意識せずに論理構造を決定することができ、これらの領域はプロセスに対するメタ操作として表せるからです。分散構造はプロセスの分散、フォールト・トレランスはプロセスのメタ実行、リアルタイムはプロセスのスケジューリングです。

オープン・コンピューティングとセキュリティの領域は、論理構造の下でプログラミング・インタフェイスによって提供されるべきでしょう。もちろん、これらの領域と論理構造の切り分けには限界がありますが、問題領域を切り分けることによって、拡張のために理解すべきものが限定できます。

拡張を行なう時に、各機能の実行順序が問題になることがあります。例えば、オブジェクト指向ではスーパークラスからサブクラスへの順にコンストラクタが実行されます。このため、継承による拡張機能を既存クラスのコンストラクタより前に実行させることはできません。デコレータ・パターンのように、コンストラクタに渡すインスタンスを拡張者が指定できる用意が必要です。

一般に、手続き型言語では既存のプログラムに拡張機能を付け加える位置を意識しなければなりません。なぜなら、機能の実行順序がプログラム上の位置によって暗黙に指示されてしまうからです。手続き型言語で並行プロセスを表現するには、並行性を明示します。例えば、 fork() を呼び出すなどです。

これに対して、並行論理言語ではプロセスの実行順序が非決定的であり、データフローによって逐次性を明示します。また、純関数型言語では論理的意味がプロセスの実行順序に依存せず、 continuation によって時系列を明示します。これらの言語は手続き型言語よりも形式化に向いており、理想的なプログラミング・インタフェイスの最下層に近いと言えます。

5 インタフェイスの個別化

理想的なインタフェイスの条件は強力さと分かりやすさです。強力なインタフェイスは多々ありますから、ここでは分かりやすいインタフェイスを考えます。

あらゆるソフトウェアはインタフェイスを提供します。アプリケーション／ミドルウェア／高級言語／ OS ／ CPU のように、個々の層において高級なものから低級なものへのインタフェイスを行ないます。

このレポートで問題にしているプログラミングのインタフェイスとしては、出来上がるソフトウェアの外観を GUI でハックするという単純なものだけでなく、プログラミングにおける抽象概念を GUI オブジェクトに具象化し、オブジェクトを操作することによって抽象実体をプログラムするプログラミング環境が既に存在します。

とは言え、これらのプログラミング環境も、 GUI の陰にプログラミング言語へのインタフェイスを持っていますから、分かりやすいプログラミング言語もまた求め続けられるべきでしょう。

人間にとって分かりやすい言語とは、馴染み深い言語です。広く使われている言語の多くが C や Java に近い構文を持っていることを証拠として挙げられます。この説を敷衍すれば、理想のプログラミング言語は\underline{各個人} にとって最も馴染み深い言語であるべきです。

インライン関数やループの展開、クラスツリーの平坦化、あるいは逆に共通部分の抽出などは現在のプログラミング・エディタでも、ある程度まで行なえます。ただ、一般に理想と言えるインタフェイスが分からないせいで、大胆なプログラム変形が避けられているだけです。

しかし、プログラミング＝コンピューティング環境の個別化が推し進められれば、もっと大胆なカスタマイズが可能になるはずです。交換されるデータは汎用のもので、自分の環境でそのデータを見る時に自分専用の変形を行なうのです。 goto の使用や波括弧の位置に関する、くだらない聖戦はなくなるでしょう。

例えば、私は研究室のホストで多くの作業を行なっていますが、ログインする端末によって画面解像度が異なり、文字が小さ過ぎたり画面からウィンドウがはみ出たりします。また、家からの接続環境には不満があるため、フロッピーディスクでデータ交換を行なっています。

分散環境が整えば、研究室の X と Emacs をカスタマイズして、いつでもどこでも自分専用のプログラム変形を行ない、理想のコンピューティング環境に近付けることもできるでしょう。

物理センサーによるロケーティング、ジェスチャーによるコマンドが普及すれば、さらなる環境の個別化も可能になるでしょう。

また例えば、 if 文の then 節と else 節のどちらを前にするか、条件の否定をとって節の順序を入れ換えるべきかという指針があります。

• 短い節が前、長い節が後

• 一般的な節が前、特殊な節が後

• 本来実行されるべき節が前、例外的な節が後

これらの指針は主にプログラムの読みやすさに関わるものですが、どちらの節を前にするかの選択は実行効率にも関わります。マシン語レベルでは、前の節には 1 つ余分なジャンプ命令が必要です。また、最近はパイプライン処理が流行していますが、投機計算されるべき分岐の選択が固定である（例えば常に前の節を選ぶ）ような簡易な CPU では、節の順序が問題になり得ます。

このような最適化は専ら人手によって行なわれています。マシン語以下のレベルでは、どちらの節がより高速に実行されるか（物理的制約）は分かるものの、どちらの節をより高速に実行すべきか（論理的意味）が分かりません。逆に高級言語では、汎用性を犠牲にしないで物理的制約を断定することができません。これは、論理構造の上に最適な物理構造を指定することができる好例です。デバッガやプロファイラも含めたプログラミング環境によって、この種の最適化は自動的に行なうことができそうです。