2011-08-25 5 views
15

여기 반사를 가속화에 게시물을 많이 호출, 예제가 있습니다. 여기가속화 반사 호출 C#을/NET

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#



제 질문은 일반적인 호출 속도를 높이는 것에 관한 것입니다. 이것이 가능합니까? 나는 추상 클래스를 구현하는 클래스를 가지고

...

내가 그들의 HandleMessage을 (이 메시지 핸들러 클래스의 목록을 구축하고 신속하게 호출되어 수행 할 작업을
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++; } 
} 

)

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, 나는 내가 이것을 잠글 수 실현, 클래스에 일반 매개 변수를 유지하고 싶습니다 매우 느립니다 개체 및 HandleMessage 메서드에서 그것을 캐스팅하지만이 일을 피하기 위해 노력하고있어.

이 작업을 빠르게 처리 할 수있는 방법이 있습니까? 현재 직접 전화보다 느린 속도입니다.

도움을 주시면 감사하겠습니다.

답변

7

C# 4를 사용하고 있습니까? 그렇다면, dynamic 사물의 속도가 빨라질 수 있습니다 : 당신은 Delegate::CreateDelegate을 사용할 수 있습니다

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

이것은 RuntimeBinderException을 던져 버리는 것 같습니다 : 'X'에 대한 가장 오버로드 된 메소드 일치가 잘못된 인수를가집니다. – Rob

+0

@Rob :'((동적) 핸들러) .HandleMessage ((동적) 메시지)'는 어떻습니까? – Gabe

+0

그게 효과가! :) 우와! – Rob

0

아니요, 슬프게도 불가능합니다. 반영 이고 MethodInfo.Invoke()도 예외는 아닙니다. 따라서 (일반) 인터페이스를 사용할 수 없으므로 전화를 걸 수 있습니까?

업데이트 수정 : 한 가지 사실은 속도를 높이는 것이지만 코딩 오버 헤드는 엄청납니다. 동적 코드 생성 및 컴파일을 사용할 수 있습니다. 즉, 리플렉션없이 메소드를 호출하는 소스 코드를 동적으로 빌드하고이를 동적으로 컴파일하고 실행한다는 것을 의미합니다. 이것은 작업을 수행하는 클래스를 작성하고 컴파일하는 데 처음에는 성능에 영향을 미치지 만 이후의 모든 호출에는 직접 호출을합니다.

+0

그것은 * * 가능하다. –

+0

코딩 오버 헤드가 "방대한"이유는 무엇입니까? 'Expression'을 사용하여 몇 줄의 코드 만 있으면됩니다. – Gabe

+0

첫째, 표현식 (또는 CodeDom)을 사용할 때 유지 보수 가능성이 문제가됩니다. 또한 생성 된 코드를 디버깅/검증하는 것이 더 어렵습니다. C# 코드를 즉시 생성하고 컴파일하는 것이 더 좋습니다. 이렇게하면 생성 된 컨트롤을보다 잘 제어 할 수 있습니다. –

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

. 이것은 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'유형의 ParameterExpression은 'Message'유형의 위임 매개 변수에 사용할 수 없습니다. ---- 이것은 첫 번째 편집 btw입니다. – Rob

0

서명을 알고있는 경우 Delegate.CreateDelegate을 사용하십시오.

서명을 모르는 경우 빠른 것을 얻는 것은 매우 까다로운 작업입니다. 속도가 필요하다면 무엇이든간에 매우 느린 Delegate.DynamicInvoke을 피하십시오.

(여기서 "느린"은 매우 상대적 임에 유의하십시오.) 실제로 이것을 최적화해야합니다. DynamicInvoke은 내 컴퓨터에서 초당 250 만 번의 호출과 비슷합니다. 이상과 같은 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; 
}