2009-04-30 5 views
7

スタックレスVMとは、システム「Cスタック」を使用する代わりに、ヒープ上に独自のスタックを維持する実装を意味します。これには継続性やシリアライズ可能な状態のような多くの利点がありますが、Cバインディング、特にC-VM-Cの種類のコールバック(またはVM-C-VM)ではいくつかの欠点もあります。スタックレスVM実装では、C統合の問題は何ですか?

これらの欠点はまさに問題ですか?誰かが本当の問題の良い例を挙げることができますか?

答えて

5

あなたは既にいくつかの欠点と利点に精通しているようです。

いくつか他の人

:それ b)は簡単言語レベル「スタックトレース」 C)簡単のようなものを構築するための基礎となる実装が任意のサポートを持っていない場合でも、 a)は、適切な末尾呼び出しの最適化をサポートすることが可能となります

私は最近、最初に.NETスタックを使用していたシンプルな "Scheme"インタプリタをC#で書いていました。私それは、明示的なスタックを使用するように再書いた - ので、おそらく次のようにあなたを助ける:

最初のバージョンは暗黙の.NETランタイムスタックを使用...最初

、それだけでクラス階層でした、異なる形式(ラムダ、レッツなど)次のインターフェイスの実装とされて:追加のために

/// <summary> 
/// Fundamental interface for resolving "symbols" subject to scoping. 
/// </summary> 
public interface IEnvironment 
{ 
    object Lookup(string name); 
    IEnvironment Extend(string name, object value); 
} 

」:あなたが期待するよう

IEnvironmentが見えた

// A "form" is an expression that can be evaluted with 
// respect to an environment 
// e.g. 
// "(* x 3)" 
// "x" 
// "3" 
public interface IForm 
{ 
    object Evaluate(IEnvironment environment); 
} 

/// <summary> 
/// A function is either a builtin function (i.e. implemented directly in CSharp) 
/// or something that's been created by the Lambda form. 
/// </summary> 
public interface IFunction 
{ 
    object Invoke(object[] args); 
} 

それは暗黙の.NETランタイムスタックを使用したときだった:私のSchemeインタプリタへの組み込みコマンドは」、私は当初、次のインタフェースを持っていました。間違いなくコードは少なくなりましたが、適切な末尾再帰のようなものを追加することは不可能でした。最も重要なのは、インタプリタがランタイムエラーの場合に "言語レベル"のスタックトレースを提供することが難しいことでした。

私はそれを明示的(ヒープ割り当て)のスタックに書き直しました。 「マップ」と「適用」、バックSchemeインタプリタに呼び出したように私は物事を実装することができるように

マイ「IFUNCTION」インタフェースは、次のように変更しなければならなかった:

/// <summary> 
/// A function that wishes to use the thread state to 
/// evaluate its arguments. The function should either: 
/// a) Push tasks on to threadState.Pending which, when evaluated, will 
/// result in the result being placed on to threadState.Results 
/// b) Push its result directly on to threadState.Results 
/// </summary> 
public interface IStackFunction 
{ 
    void Evaluate(IThreadState threadState, object[] args); 
} 

そしてIFormに変更しました

/// <summary> 
/// The state of the interpreter. 
/// The implementation of a task which takes some arguments, 
/// call them "x" and "y", and which returns an argument "z", 
/// should follow the following protocol: 
/// a) Call "PopResult" to get x and y 
/// b) Either 
/// i) push "z" directly onto IThreadState using PushResult OR 
/// ii) push a "task" on to the stack which will result in "z" being 
///  pushed on to the result stack. 
/// 
/// Note that ii) is "recursive" in its definition - that is, a task 
/// that is pushed on to the task stack may in turn push other tasks 
/// on the task stack which, when evaluated, 
/// ... ultimately will end up pushing the result via PushResult. 
/// </summary> 
public interface IThreadState 
{ 
    void PushTask(ITask task); 
    object PopResult(); 
    void PushResult(object result); 
} 

をそしてITASKは次のとおりです:0:次のようにIThreadStateがある

public interface IForm 
{ 
    void Evaluate(IEnvironment environment, IThreadState s); 
} 
public interface ITask 
{ 
    void Execute(IThreadState s); 
} 

そして、私のメインの「イベント」ループは次のとおりです。

ThreadState threadState = new ThreadState(); 
threadState.PushTask(null); 
threadState.PushTask(new EvaluateForm(f, environment)); 
ITask next = null; 

while ((next = threadState.PopTask()) != null) 
    next.Execute(threadState); 

return threadState.PopResult(); // Get what EvaluateForm evaluated to 

EvaluateFormは、特定の環境でIForm.Evaluateを呼び出すだけの作業です。

個人的には、この新しいバージョンは実装の観点から作業するほうがはるかに「面白い」ことがわかりました。スタックトレースを取得しやすく、完全な継続を実現するのは簡単です(ただし... C#スタックを使用するのではなく、私の "スタック"永続リンクリストを作成する必要があります。また、ITaskは "Call-continuation"タスクを持つことができるように、新しいThreadStateを変更するのではなく "返す"

基本的には、基本的な言語の実装に依存することは少なくなります。

私が見つけることができる唯一の欠点はパフォーマンスです...しかし、私の場合、それは単なる通訳者なので、とにかくパフォーマンスについてはあまり気にしません。

私も再書き込みKAI C++コンパイラの著者の一人で、スタックで反復コードとして再帰的なコードの利点に、この非常に素晴らしい記事にあなたを指したい:Considering Recursion

+0

実際、ネイティブコードの統合のみを考慮すると、問題点がありました。しかし、物語のおかげで。 –

1

した後、E- Steve Dekorte(Ioプログラミング言語の著者)とKonstantin Oleninとのメール会話で、私は問題とそれに対する(部分的な)解決策を見つけました。 VMからC関数への呼び出しを想像してください。これはVMメソッドを呼び出します。 VMがコールバックを実行している期間中、VMの状態の一部は、VMスタックの外側にあります。その時点でVMの状態を保存すると、次にVMがロードされたときに状態を正しく復元できないことが保証されます。

解決策は、VMをメッセージ受信アクターとしてモデル化することです.VMは非同期通知をネイティブコードに送信し、ネイティブコードはVMに非同期通知を送信することができます。つまり、シングルスレッド環境では、VMが制御権を獲得したときに、VM実行時に関係のないデータを除き、追加の状態はその外部に格納されません。

これは、どのような状況でもVMの状態を正しく復元できるわけではありませんが、少なくとも、信頼できるシステムを構築することは可能です。

関連する問題