2010-05-23 6 views
5

कुछ नाराज परीक्षणों के बाद मुझे पता चला है कि मेरा कार्यान्वयन बहुत अधिक पुनरावर्तन संभाल नहीं सकता है। हालांकि मैंने फ़ायरफ़ॉक्स में कुछ परीक्षणों के बाद मुझे पाया कि यह मूल रूप से सोचा जाने से कहीं अधिक आम हो सकता है। मेरा मानना ​​है कि मूल समस्या यह है कि मेरे कार्यान्वयन को फ़ंक्शन कॉल करने के लिए 3 कॉल की आवश्यकता होती है। पहला कॉल Call नामक विधि के लिए बनाया गया है जो सुनिश्चित करता है कि कॉल एक कॉल करने योग्य ऑब्जेक्ट में किया जा रहा है और संदर्भों के किसी भी तर्क का मान प्राप्त करता है। दूसरी कॉल Call नामक विधि में बनाई गई है जिसे ICallable इंटरफ़ेस में परिभाषित किया गया है। यह विधि नया निष्पादन संदर्भ बनाता है और अगर यह नहीं बनाया गया है तो लैम्ब्डा अभिव्यक्ति बनाता है। अंतिम कॉल लैम्बडा को बनाया जाता है कि फ़ंक्शन ऑब्जेक्ट encapsulates। स्पष्ट रूप से एक फ़ंक्शन कॉल करना काफी भारी है, लेकिन मुझे यकीन है कि थोड़ी सी ट्वीविंग के साथ मैं इस कार्यान्वयन का उपयोग करते समय रिकर्सन को व्यवहार्य टूल बना सकता हूं।मैं अपने ईसीएमएस्क्रिप्ट कार्यान्वयन की रिकर्सन क्षमताओं को कैसे सुधार सकता हूं?

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

मुझे लगता है कि पूंछ रिकर्सन को लूप में बदलने के लिए अब के दायरे से बाहर है? इस तरह आप पूरी तरह से कॉल करने से बच सकते हैं। –

+0

@DrJokepu - मैं अपने दिमाग के पीछे पूंछ रिकर्सन का उपयोग करने का विचार रख रहा हूं, लेकिन मैं सामान्य प्रदर्शन सुधार के रूप में कॉल को कम भारी बनाने के सुझावों की भी तलाश कर रहा हूं। इसके अलावा मुझे विश्वास नहीं है कि पूंछ रिकर्सन उन मामलों में ठीक से लागू किया जा सकता है जहां समारोह की जटिलता बहुत अच्छी है। – ChaosPandion

+0

वैसे ऐसा लगता है कि यह कुछ भी अनावश्यक नहीं कर रहा है, क्या आपने इसे प्रोफाइलर के साथ चलाने का प्रयास किया है? मेरा मतलब है, सीएलआर में फंक्शन कॉल (रिलीज मोड में) बहुत महंगा नहीं है (दुर्भाग्य से दूसरा कॉल जेआईटी द्वारा रेखांकित करने के लिए थोड़ा वसा है) इसलिए मुझे संदेह है कि यही भारी है। शायद संदर्भ में कुछ। GetValue() या कुछ? एक प्रोफाइलर निश्चित रूप से बहुत उपयोगी होगा। –

उत्तर

2

मुझे विश्वास नहीं है कि यह काम करना कितना आसान था। असल में मेरे कंपाइलर में मैं यह देखने के लिए जांच करता हूं कि फ़ंक्शन स्वयं कॉल करने के परिणाम को वापस कर रहा है या नहीं। यदि ऐसा है तो मैं इसके बजाय उन तर्कों को वापस कर दूंगा जो पारित किए जा रहे हैं। तो मैं बस किसी भी संदर्भ मूल्यों को पकड़ता हूं और बैकिंग लैम्ब्डा को फिर से आमंत्रित करता हूं। इस जगह के साथ मैं लाखों रिकर्सिव कॉल करने में सक्षम था।

मैं इस समाधान को प्रेरणा देने के लिए DrJokepu धन्यवाद देना चाहता हूं।

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; 
} 
संबंधित मुद्दे