2011-08-25 14 views
15

प्रतिबिंब को तेज करने पर पदों के बहुत सारे का आह्वान, उदाहरण यहाँ हैं:।बढ़ाता प्रतिबिंब आह्वान सी #/नेट

Speeding up Reflection API with delegate in .NET/C#

https://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/

और यहाँ:

Example : Speeding up Reflection API with delegate in .NET/C#



मेरा प्रश्न सामान्य आवेषण को तेज करने के बारे में है। क्या यह वास्तव में संभव है?

मैं एक अमूर्त वर्ग और एक वर्ग है जो इसे लागू करता है ...

public abstract class EncasulatedMessageHandler<T> where T : Message 
{ 
    public abstract void HandleMessage(T message); 
} 

public class Handler : EncasulatedMessageHandler<MyMessageType> 
{ 
    public int blat = 0; 
    public override void HandleMessage(MyMessageType message) { blat++; } 
} 

क्या मैं इन संदेश हैंडलर वर्गों की सूची और उनकी HandleMessage (निर्माण और जल्दी से आह्वान किया गया है क्या करना चाहते हैं)

object handler = Activator.CreateInstance(typeof(Handler)); // Ignore this, this is done up front. 

MethodInfo method = type.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public); 

Action<object> hook = new Action<object>(delegate(object message) 
{ 
    method.Invoke(handler, new object[] { message }); 
}); 

// Then when I want to invoke it: 

hook(new MyMessageType()); 

:

पल में, मैं कुछ लगभग इस बात यह है कि कर रहा हूँ

पूरी बात नहीं है यही कारण है कि है, लेकिन यह महत्वपूर्ण सामग्री है ...

method.Invoke बहुत धीमी है, मैं कक्षा पर सामान्य मापदंडों रखना चाहते हैं, मुझे लगता है मैं करने के लिए इस पर ताला लगा सकता है एहसास ऑब्जेक्ट करें और इसे हैंडल मैसेज विधि में डालें, लेकिन मैं ऐसा करने से बचने की कोशिश कर रहा हूं।

क्या यह कुछ भी करने के लिए मैं कर सकता हूं? वर्तमान में प्रत्यक्ष कॉल की तुलना में परिमाण धीमा है।

किसी भी मदद की सराहना की जाएगी।

उत्तर

7

क्या आप सी # 4 का उपयोग कर रहे हैं? यदि हां, तो dynamic चीज़ों को गति कर सकते हैं:

Action<object> hook = message => ((dynamic)handler).HandleMessage((dynamic)message); 
+0

यह रनटाइमबिन्डर अपवाद को फेंकने लगता है: 'एक्स' के लिए सबसे अच्छा ओवरलोडेड विधि मिलान में कुछ अमान्य तर्क हैं। – Rob

+0

@Rob: '((गतिशील) हैंडलर के बारे में क्या) .हैंडलमेसेज (गतिशील) संदेश)'? – Gabe

+0

यह काम करता है! :) जूट! – Rob

0

नहीं, यह (दुख की बात है) संभव नहीं है। प्रतिबिंब धीमा और MethodInfo.Invoke() कोई अपवाद नहीं है। क्या आप (जेनेरिक) इंटरफेस का उपयोग नहीं कर सकते थे और इस प्रकार सीधी कॉल?

संपादित करें अद्यतन: एक बात वास्तव में इस गति अप करने के लिए मन में आता है, लेकिन कोडिंग भूमि के ऊपर बड़े पैमाने पर है: आप गतिशील कोड पीढ़ी और संकलन का उपयोग कर सकते हैं। इसका मतलब गतिशील रूप से स्रोत कोड का निर्माण करना होगा जो प्रतिबिंब के बिना विधि को कॉल करेगा, गतिशील रूप से संकलित और इसे निष्पादित करेगा। इसका मतलब आपके काम करने वाले वर्गों को बनाने और संकलित करने के लिए प्रारंभिक प्रदर्शन प्रभाव होगा, लेकिन फिर आपके पास प्रत्येक आगामी कॉल के लिए सीधी कॉल होगी।

+0

यह * * संभव है। –

+0

कोडिंग ओवरहेड "भारी" क्यों है? मैं 'अभिव्यक्ति' का उपयोग कर कोड की केवल दो पंक्तियों की अपेक्षा करता हूं। – Gabe

+0

सबसे पहले, अभिव्यक्तियों (या कोडडॉम) का उपयोग करते समय यह रखरखाव में थकाऊ है। जेनरेट कोड को डिबगिंग/मान्य करना भी असंभव है। फ्लाई पर सी # कोड उत्पन्न करना और इसे संकलित करना बेहतर है। यह आपको उत्पन्न नियंत्रण पर बेहतर नियंत्रण प्रदान करता है। –

8

Delegate.CreateDelegate() का उपयोग करना बहुत तेज़ होना चाहिए। आप वास्तविक समारोह, नहीं एक प्रतिनिधि है कि Invoke() कॉल करने के लिए एक सूचक के साथ खत्म हो जाएगा। एक ही कक्षा में

object handler = Activator.CreateInstance(typeof(Handler)); 
var handlerType = handler.GetType(); 
var method = handlerType.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public); 
var paramType = handlerType.GetGenericArguments()[0]; 

// invoke the MakeHandleMessageDelegate method dynamically with paramType as the type parameter 
// NB we're only doing this once 
Action<object> hook = (Action<object>) this.GetType().GetMethod("MakeHandleMessageDelegate") 
      .MakeGenericMethod(paramType) 
      .Invoke(null, new [] { handler }); 

निम्नलिखित सामान्य पद्धति जोड़ें:

इस प्रयास करें। हम इसे गतिशील रूप से उपरोक्त कहते हैं क्योंकि हम संकलन समय पर प्रकार पैरामीटर नहीं जानते हैं।

public static Action<object> MakeHandleMessageDelegate<T>(object target) 
{ 
    var d = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), target, "HandleMessage"); 

    // wrap the delegate another that simply casts the object parameter to the required type 
    return param => d((T)param); 
} 

फिर आप एक प्रतिनिधि है कि आवश्यक प्रकार के पैरामीटर डाले तो HandleMessage प्रणाली को बुलाती है।

+1

मुझे इसे मारो, लेकिन आपको अपने प्रश्न से अधिक निकटता से मिलान करने के लिए 'MethodInfo' अधिभार का उपयोग करना चाहिए। –

+0

यदि आप मेरा जवाब देखते हैं तो आप देखेंगे कि उसकी समस्या इस से थोड़ा अधिक जटिल है (जेनेरिक पैरामीटर के कारण)। –

+0

धन्यवाद जोनाथन। मैं देख सकता हूं कि समस्या क्या है।मैंने एक और समाधान दिखाने के लिए मेरा संपादन किया है जिसमें अभिव्यक्ति शामिल नहीं है। उम्मीद है कि वह काम करता है। –

6

आप Delegate::CreateDelegate उपयोग कर सकते हैं। यह Invoke() से काफी तेज है।

var handler = Activator.CreateInstance(typeof(Handler)); 
var method = type.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public); 
var hook = (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), handler, method); 

// Then when you want to invoke it: 
hook(new MyMessageType()); 

यह बेंचमार्क के लिए स्वतंत्र महसूस, लेकिन मैं इसे पहले बेंच पर है और यह काफी तेजी थी।

संपादित करें: अब मैं आपकी समस्या देखता हूं, आप इसे सुझाए गए तरीके से नहीं कर सकते हैं।

आप एक प्रतिनिधि है कि आप के लिए आह्वान करता संकलित करने के लिए भाव का उपयोग कर सकते, इस बहुत तेजी से हो जाएगा:

var type = typeof(Handler); 
var instance = Activator.CreateInstance(type); 
var method = type.GetMethod("HandleMessage", BindingFlags.Instance | BindingFlags.Public); 

var originalType = type; 
// Loop until we hit the type we want. 
while (!(type.IsGenericType) || type.GetGenericTypeDefinition() != typeof(EncasulatedMessageHandler<>)) 
{ 
    type = type.BaseType; 
    if(type == null) 
     throw new ArgumentOutOfRangeException("type"); 
} 

var messageType = type.GetGenericArguments()[0]; // MyMessageType 

// Use expression to create a method we can. 
var instExpr = Expression.Parameter(typeof(object), "instance"); 
var paramExpr = Expression.Parameter(typeof(Message), "message"); 
// (Handler)instance; 
var instCastExpr = Expression.Convert(instExpr, originalType); 
// (MyMessageType)message 
var castExpr = Expression.Convert(paramExpr, messageType); 
// ((Handler)inst).HandleMessage((MyMessageType)message) 
var invokeExpr = Expression.Call(instCastExpr, method, castExpr); 
// if(message is MyMessageType) ((Handler)inst).HandleMessage((MyMessageType)message); 
var ifExpr = Expression.IfThen(Expression.TypeIs(paramExpr, messageType), invokeExpr); 

// (inst, message) = { if(message is MyMessageType) ((Handler)inst).HandleMessage((MyMessageType)message); } 
var lambda = Expression.Lambda<Action<object, Message>>(ifExpr, instExpr, paramExpr); 
var compiled = lambda.Compile(); 
Action<Message> hook = x => compiled(instance, x); 

hook(new MyMessageType()); 

संपादित करें: ऊपर मेरी अभिव्यक्ति उदाहरण से अलावा, निम्नलिखित भी काम करेंगे - और मैं इन प्रकार के परिदृश्यों में क्या करता हूं।

var instance = (IEncapsulatedMessageHandler)Activator.CreateInstance(typeof(Handler)); 
instance.HandleMessage(new MyMessageType()); 

public class Message { } 

public class MyMessageType : Message { } 

public interface IEncapsulatedMessageHandler 
{ 
    void HandleMessage(Message message); 
} 

public abstract class EncasulatedMessageHandler<T> : IEncapsulatedMessageHandler where T : Message 
{ 
    public abstract void HandleMessage(T message); 

    void IEncapsulatedMessageHandler.HandleMessage(Message message) 
    { 
     var msg = message as T; 
     if (msg != null) 
      HandleMessage(msg); 
    } 
} 

public class Handler : EncasulatedMessageHandler<MyMessageType> 
{ 
    public override void HandleMessage(MyMessageType message) 
    { 
     Console.WriteLine("Yo!"); 
    } 
} 
+0

यह एक ArgumentException फेंकने लगता है: लक्ष्य विधि को बाध्य करने में त्रुटि। ऑब्जेक्ट सामान्य तर्क से जुड़ने के लिए प्रतीत नहीं होता है। – Rob

+0

@Rob संपादन को देखो, अब मुझे आपकी समस्या दिखाई दे रही है। –

+0

यह लाइन पर असफल प्रतीत होता है: var lambda = Expression.Lambda > (ifExpr, instExpr, paramExpr); ---- पैरामीटर का प्रकार 'MyMessageType' का प्रकार 'संदेश' के प्रतिनिधि पैरामीटर के लिए उपयोग नहीं किया जा सकता ---- यह आपका पहला संपादन बीटीडब्ल्यू था। – Rob

0

यदि आप हस्ताक्षर जानते हैं, तो Delegate.CreateDelegate का उपयोग करें।

यदि आप हस्ताक्षर नहीं जानते हैं, तो यह तेज़ी से कुछ हासिल करना बहुत मुश्किल है। यदि आपको गति की आवश्यकता है, तो आप जो कुछ भी करते हैं, Delegate.DynamicInvoke से बचने का प्रयास करें जो बेहद धीमी है।

(ध्यान दें कि "धीमा" बहुत सापेक्ष है। सुनिश्चित करें कि आपको वास्तव में इसे अनुकूलित करने की आवश्यकता है। DynamicInvoke प्रति सेकंड 2.5 मिलियन आमंत्रण (मेरी मशीन पर) की तरह कुछ है, जो काफी तेज़ है। नीचे कार्यान्वयन है प्रति सेकंड 110 + लाख से की तरह आमंत्रण और Method.Invoke से तेज है।)

मैं an article कि यह करने के लिए (संकलन समय पर हस्ताक्षर जानने के बिना तेजी से एक विधि आह्वान) एक तरह से चर्चा करता पाया। कार्यान्वयन का मेरा संस्करण यहां है। अजीब मुद्दा यह है कि आप एक लैम्ब्डा बना सकते हैं जो आमंत्रण का प्रतिनिधित्व करता है, लेकिन आप उस लैम्ब्डा के हस्ताक्षर को नहीं जान पाएंगे और उसे गतिशील रूप से (धीरे-धीरे) कॉल करना होगा। लेकिन इसके बजाय, आप लैम्ब्डा में दृढ़ता से टाइप किए गए आमंत्रण को सेंक सकते हैं, जिसमें लैम्ब्डा विशिष्ट विधि के बजाय आमंत्रण के कार्य का प्रतिनिधित्व करती है। (लैम्ब्डा एक Func<object, object[], object> किया जा रहा है, जहां आप एक वस्तु और कुछ मान गुजरती हैं और वापसी मान वापस पाने के समाप्त होता है।)

public static Func<object, object[], object> ToFastLambdaInvocationWithCache(
    this MethodInfo pMethodInfo 
) { 
    Func<object, object[], object> cached; 
    if (sLambdaExpressionsByMethodInfoCache.TryGetValue(pMethodInfo, out cached)) 
     return cached; 

    var instanceParameterExpression = Expression.Parameter(typeof(object), "instance"); 
    var argumentsParameterExpression = Expression.Parameter(typeof(object[]), "args"); 

    var index = 0; 
    var argumentExtractionExpressions = 
     pMethodInfo 
     .GetParameters() 
     .Select(parameter => 
     Expression.Convert(
      Expression.ArrayAccess(
       argumentsParameterExpression, 
       Expression.Constant(index++) 
      ), 
      parameter.ParameterType 
     ) 
    ).ToList(); 

    var callExpression = pMethodInfo.IsStatic 
     ? Expression.Call(pMethodInfo, argumentExtractionExpressions) 
     : Expression.Call(
     Expression.Convert(
      instanceParameterExpression, 
      pMethodInfo.DeclaringType 
     ), 
     pMethodInfo, 
     argumentExtractionExpressions 
    ); 

    var endLabel = Expression.Label(typeof(object)); 
    var finalExpression = pMethodInfo.ReturnType == typeof(void) 
     ? (Expression)Expression.Block(
      callExpression, 
      Expression.Return(endLabel, Expression.Constant(null)), 
      Expression.Label(endLabel, Expression.Constant(null)) 
     ) 
     : Expression.Convert(callExpression, typeof(object)); 

    var lambdaExpression = Expression.Lambda<Func<object, object[], object>>(
     finalExpression, 
     instanceParameterExpression, 
     argumentsParameterExpression 
    ); 
    var compiledLambda = lambdaExpression.Compile(); 
    sLambdaExpressionsByMethodInfoCache.AddOrReplace(pMethodInfo, compiledLambda); 
    return compiledLambda; 
} 
संबंधित मुद्दे