Next: Up: Previous:

より手の込んだスレッド管理方式

より手の込んだ方法はMohr,Kranz,Halstead [18], [32] に よって最初に述べられた報告に基づいている. その報告は, スレッド生成は, 本当に必要だとわかったときに 初めて本当のforkをさかのぼって 行なうために, 単に最小の情報を残しておくことだけを すればよいということである. より正確に言えば, fのボディを評価するスレッドを 生成するときには, fの評価を全く 逐次呼び出しのように続ける. もし f がブロックしたときにfの終了を待たずに f の計算を再開するための機構が用意されている. もしfがまったくブロックしないならば, スレッド生成の コストはおおまかにいえば手続き呼びだしおよび潜在forkポイントを 示すためのデスクリプタを書き込むことだけである. 基本的な構造--最小のforkオーバヘッドと逆戻り仕事生成-- は後に続く多くの研究 [20], [38], [37], [42] において, さらなる明確化と改良を 加えた異なるやり方ではあるものの至る所で見られる.

この基本的なアイデアはスレッドを管理するための単純な タスクキューアプローチへのいくつかの改良に対して門を開いている. 一つめに, 新しいスレッドのフレームをスタックから確保することは 一般自由記憶から確保し, タスクプールに入れるよりも より効率的である. もし我々にCで説明する機会が与えられるならば, これはCの手続き呼びだしによって表現されると言える. 二つめとして, スケジューリングの順番が 固定されており, LIFOであるために, スレッドがブロックせずに終了 したときには, 結果の値をレジスタで返すことができる. これは 次に走るスレッドがcallerであることがわかっているからである. スレッドがブロックしたあとに結果の値を返すためには プロトコルを工夫しなくてはならない. 三番目に, スレッド生成においてさえもcallee-save registerを 利用する機会を持っていること. これもやはり, calleeが ブロックせず終了したときにはcalleeのすぐ後にcallerがスケジュール されることがわかっており, callerのためにcallee-save registerを 回復すべき場所がわかっているからである.

本研究の実行方式は同じ主張に基づいており, 著者らの 以前の研究 [43] およびConcertのhybrid execution model [37] に最も近い. 我々の研究と全てのこれまでの研究との差は, どこにその 機構が実装されているかということである. 現在存在する方式 は自前のフレーム管理プロトコルを設計し, 逐次コンパイラ--最適化Cコンパイラ--の直接的な再利用を 許していない. 一方, StackThreadsは伝統的なCスタックを扱っている. コンパイラは直接的に逐次の計算をCに対応させ, コンテキストスイッチはランタイムにライブラリルーチンを呼ぶだけである.

手続き f を評価するスレッドがブロックしたとき, [43][37] では手続きのスタックフレームを単にセーブし, 現在の フレームのすぐ下のフレームを再開する. 両方とも この機構をアセンブリライクなCコードを生成することで行なっている. ソース言語の手続きは, インライン展開を無視すれば, Cの手続きにコンパイルされる. 各潜在ブロックポイントには コンパイラが全ての生きている変数をヒープフレームにセーブし, リターンするコード列を生成する. ブロックした点から計算を再開するために また別のコード列が生成される. そのコード列はメモリから 生きている変数をロードし, ブロックした点に「goto」する. 少なくとも著者らの [43] における経験では, この実装戦略は コスト効率という見地から言えばそれほどうまくいく とは言えない. まず最初に, 生成されるCコードは非常に大きく, 埋め込まれたスイッチのためのコード列に起因する あいまいな制御/データフローを持っている. これにより, Cコンパイラは最適化に失敗する傾向がある. 二番目に, 正しい実行を保証するためにlive-rangeなどの 低レベルの解析を行なわなければならないため, コンパイラの開発コストが高い. どちらの要素とも, Cコードを生成することに対するいくつかの動機づけ --コンパイラの開発の簡単化とバックエンドによる最適化--を失わせる.

LazyThreads [20] はフレーム管理とスレッド中断について, 似ているが異なるアプローチを取っている. フレームは彼らが呼ぶところの StackLet の単位の中から確保される. 一つの stacklet はいくつかのフレームを保持することができ, 普通のスタックのサイズよりもはるかに小さい連続領域である. ブロックしたスレッドはスタックフレームをヒープにコピーせずに caller を再開する. その代わりに, 各手続きは現在の フレームの上に使用可能な領域があるかどうかをチェックする. もしないときは, その呼び出しが逐次か, 並列かにかかわらず, 新しい stacklet が確保される. LazyThreads の実装は実行時の データ構造を一から設計することを余儀なくされており, GNU C コンパイラを修正することで行なわれている.



Mitsubishi Research Institute,Inc.
Mon Feb 24 19:27:36 JST 1997