2010-05-23 8 views
5

Nach einigen erneuten Tests habe ich festgestellt, dass meine Implementierung nicht sehr viel Rekursion verarbeiten kann. Obwohl ich nach einigen Tests in Firefox festgestellt habe, dass dies häufiger vorkommt als ursprünglich angenommen. Ich glaube, dass das grundlegende Problem ist, dass meine Implementierung 3 Aufrufe benötigt, um einen Funktionsaufruf zu tätigen. Der erste Aufruf erfolgt an eine Methode mit dem Namen Call, die sicherstellt, dass der Aufruf an ein aufrufbares Objekt erfolgt und den Wert aller Argumente bezieht, die Referenzen sind. Der zweite Aufruf erfolgt an eine Methode mit dem Namen Call, die in der Schnittstelle ICallable definiert ist. Diese Methode erstellt den neuen Ausführungskontext und erstellt den Lambda-Ausdruck, wenn er nicht erstellt wurde. Der letzte Aufruf erfolgt an das Lambda, das das Funktionsobjekt einkapselt. Es ist ziemlich schwer, einen Funktionsaufruf zu machen, aber ich bin mir sicher, dass ich mit ein wenig Optimierung die Rekursion zu einem brauchbaren Werkzeug machen kann, wenn ich diese Implementierung verwende.Wie kann ich die Rekursionsfähigkeiten meiner ECMAScript-Implementierung verbessern?

+0

Ich nehme an, Umwandlung von Tail-Rekursion in Schleifen ist für den Augenblick nicht im Fokus? Auf diese Weise könnten Sie vermeiden, vollständig anzurufen. –

+0

@DrJokepu - Ich habe die Idee der Schwanzrekursion in meinem Hinterkopf behalten, aber ich suche auch nach Vorschlägen, wie man die Anrufe selbst als eine allgemeine Leistungsverbesserung weniger schwer macht. Ich glaube auch nicht, dass die Tail-Rekursion in Fällen, in denen die Komplexität der Funktion zu groß ist, richtig implementiert werden kann. – ChaosPandion

+0

Nun, es sieht nicht so aus, als würde es irgendetwas unnötig machen, hast du es mit einem Profiler versucht? Ich meine, Funktionsaufrufe (im Release-Modus) sind in CLR nicht sehr teuer (leider ist der zweite Call etwas zu fett, um vom JIT inline geschaltet zu werden), also bezweifle ich, dass das deshalb schwer ist. Vielleicht etwas in Reference.GetValue() oder etwas? Ein Profiler wäre definitiv sehr hilfreich. –

Antwort

2

Ich kann nicht glauben, wie einfach das zu arbeiten war. Grundsätzlich überprüfe ich in meinem Compiler, ob die Funktion das Ergebnis des Aufrufs selbst zurückgibt. Wenn dem so ist, gebe ich stattdessen die Argumente zurück, die übergeben werden. Dann greife ich einfach irgendwelche Referenzwerte und lade das Hintergrundlambda erneut ein. Damit konnte ich Millionen von rekursiven Calls machen.

Ich möchte DrJokepu für die Inspiration dieser Lösung danken.

public object Call(object thisObject, object[] arguments) 
{ 
    var lexicalEnviroment = Scope.NewDeclarativeEnviroment(); 
    var variableEnviroment = Scope.NewDeclarativeEnviroment(); 
    var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject; 
    var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding); 
    var result = default(object); 
    var callArgs = default(object[]); 

    Engine.EnterContext(newContext); 
    while (true) 
    { 
     result = Function.Value(newContext, arguments); 
     callArgs = result as object[]; 
     if (callArgs == null) 
     { 
      break; 
     } 
     for (int i = 0; i < callArgs.Length; i++) 
     { 
      callArgs[i] = Reference.GetValue(callArgs[i]); 
     } 
     arguments = callArgs; 
    } 
    Engine.LeaveContext(); 

    return result; 
} 
Verwandte Themen