2010-05-23 17 views
5

Bazı yeniden testlerden sonra, uygulamamın çok fazla özyinelemeyi başaramadığını tespit ettim. Firefox'ta birkaç test yaptıktan sonra, bunun aslında düşündüğümden daha yaygın olabileceğini keşfettim. Temel sorunun, benim uygulamamın bir işlev çağrısı yapmak için 3 çağrı gerektirdiğine inanıyorum. İlk çağrı, çağrının çağrılabilir bir nesneye yapılmasını ve referans olan argümanların değerini almasını sağlayan Call isimli bir yönteme yapılır. İkinci çağrı, ICallable arabiriminde tanımlanan Call isimli bir yönteme yapılır. Bu yöntem, yeni yürütme bağlamını oluşturur ve oluşturulmamışsa, lambda ifadesini oluşturur. Son çağrı, fonksiyon nesnesinin kapsüllediği lambda yapılır. Açıkça bir işlev çağrısı yapmak oldukça ağırdır, ancak bu uygulamayı kullanırken yinelemenin uygulanabilir bir araç yapabileceğine eminim.ECMAScript uygulamamın yineleme yeteneklerini nasıl geliştirebilirim?

public static object Call(ExecutionContext context, object value, object[] args) 
{ 
    var func = Reference.GetValue(value) as ICallable; 
    if (func == null) 
    { 
     throw new TypeException(); 
    } 
    if (args != null && args.Length > 0) 
    { 
     for (int i = 0; i < args.Length; i++) 
     { 
      args[i] = Reference.GetValue(args[i]); 
     } 
    } 
    var reference = value as Reference; 
    if (reference != null) 
    { 
     if (reference.IsProperty) 
     { 
      return func.Call(reference.Value, args); 
     } 
     else 
     { 
      return func.Call(((EnviromentRecord)reference.Value).ImplicitThisValue(), args); 
     } 
    } 
    return func.Call(Undefined.Value, args); 
} 

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); 
    Engine.EnterContext(newContext); 
    var result = Function.Value(newContext, arguments); 
    Engine.LeaveContext(); 
    return result; 
} 
+0

Kuyruk dönüşünü döngülere dönüştürmenin sanırım şu anda kapsam dışı mı? Böylece tamamen aramadan kaçınabildin. –

+0

@DrJokepu - Aklımın arkasındaki kuyruk özlemini kullanma fikrini sürdürmekteyim, aynı zamanda genel bir performans artışı olarak çağrıların kendilerini daha az nasıl ağırlaştıracaklarına dair öneriler de arıyorum. Ayrıca, fonksiyonun karmaşıklığının çok büyük olduğu durumlarda kuyruk yinelemesinin düzgün bir şekilde uygulanabileceğine inanmıyorum. – ChaosPandion

+0

Gereksiz bir şey yapmak gibi görünmüyor, bir profiler ile çalıştırmayı denediniz mi? Yani, işlev çağrıları (Release modunda) CLR'de çok pahalı değil (maalesef ikinci Çağrı, JIT tarafından belirtilmek için biraz fazla şişmandır) bu yüzden bu yüzden ağırdır. Belki de Reference.GetValue() veya başka bir şey? Bir profiler kesinlikle çok yararlı olurdu. –

cevap

2

Bunun işe yaramasının ne kadar kolay olduğuna inanamıyorum. Temel olarak derleyicimde, işlevin kendisini aramanın sonucunu döndürüp döndürmediğini kontrol ediyorum. Öyleyse bunun yerine geçirilmekte olan argümanları iade ediyorum. Sonra sadece referans değerlerini alıyorum ve destek lambda'yı yeniden çağırıyorum. Bu ile milyonlarca özyinelemeli arama yapabildim.

Bu çözüm için DrJokepu'a teşekkür ederim.

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; 
} 
İlgili konular