C# MethodInfo.Invoke性能问题

C# MethodInfo.Invoke性能问题,c#,.net,invoke,C#,.net,Invoke,我在文件中读写数据。文件中的数据可以是浮点、双精度、整数等。直到运行时才知道类型。我将文件中存储的数据类型称为Tin。数据被读入Tout类型的数组或从中写入。直到运行时才知道这种类型 代码序列是这样的。在已知的开放方法Tin和Tout中,我们可以为已知的数据类型创建读写方法 Open(...) { MethodInfo ReadMethod = typeof(...)GetMethod("ReadGeneric").MakeGenericMethod(new Type[] {typeof(

我在文件中读写数据。文件中的数据可以是浮点、双精度、整数等。直到运行时才知道类型。我将文件中存储的数据类型称为Tin。数据被读入Tout类型的数组或从中写入。直到运行时才知道这种类型

代码序列是这样的。在已知的开放方法Tin和Tout中,我们可以为已知的数据类型创建读写方法

Open(...)
{
   MethodInfo ReadMethod = typeof(...)GetMethod("ReadGeneric").MakeGenericMethod(new Type[] {typeof(Tin), typeof(Tout)}));
}
读写循环重复数百万次,并依靠反射来调用适当的方法,如下所示

Read loop
{
   var values = (Tout[])ReadMethod.Invoke(this,new object[]{index});
   process ...
}
当使用性能分析器检查这段代码时,我发现,如果只调用运行时读写方法,则c collosal的数量会增加


如何加快速度。

是的,这是因为反射API比直接方法调用慢数千倍。然而,有一些有趣的技术可以解决这个问题。看看乔恩·斯基特的文章

有一个静态的设置成本,但是一旦您完成了,重复调用委托的时间就相当于虚拟方法调用


还有一些是为了实现同样的目标

这可以帮你做任何事,几乎和直接电话一样快

using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

public class FastMethodInfo
{
    private delegate object ReturnValueDelegate(object instance, object[] arguments);
    private delegate void VoidDelegate(object instance, object[] arguments);

    public FastMethodInfo(MethodInfo methodInfo)
    {
        var instanceExpression = Expression.Parameter(typeof(object), "instance");
        var argumentsExpression = Expression.Parameter(typeof(object[]), "arguments");
        var argumentExpressions = new List<Expression>();
        var parameterInfos = methodInfo.GetParameters();
        for (var i = 0; i < parameterInfos.Length; ++i)
        {
            var parameterInfo = parameterInfos[i];
            argumentExpressions.Add(Expression.Convert(Expression.ArrayIndex(argumentsExpression, Expression.Constant(i)), parameterInfo.ParameterType));
        }
        var callExpression = Expression.Call(!methodInfo.IsStatic ? Expression.Convert(instanceExpression, methodInfo.ReflectedType) : null, methodInfo, argumentExpressions);
        if (callExpression.Type == typeof(void))
        {
            var voidDelegate = Expression.Lambda<VoidDelegate>(callExpression, instanceExpression, argumentsExpression).Compile();
            Delegate = (instance, arguments) => { voidDelegate(instance, arguments); return null; };
        }
        else
            Delegate = Expression.Lambda<ReturnValueDelegate>(Expression.Convert(callExpression, typeof(object)), instanceExpression, argumentsExpression).Compile();
    }

    private ReturnValueDelegate Delegate { get; }

    public object Invoke(object instance, params object[] arguments)
    {
        return Delegate(instance, arguments);
    }
}
使用System.Collections.Generic;
使用System.Linq.Expressions;
运用系统反思;
公共类FastMethodInfo
{
私有委托对象ReturnValueDelegate(对象实例,对象[]参数);
私有委托void VoidDelegate(对象实例,对象[]参数);
公共快速方法信息(方法信息方法信息)
{
var instanceExpression=Expression.Parameter(typeof(object),“instance”);
var argumentsPression=Expression.Parameter(typeof(object[]),“arguments”);
var argumentExpressions=新列表();
var parameterInfo=methodInfo.GetParameters();
对于(变量i=0;i{voidDelegate(实例,参数);返回null;};
}
其他的
Delegate=Expression.Lambda(Expression.Convert(callExpression,typeof(object)),instanceExpression,argumentsPression.Compile();
}
private ReturnValueDelegate委托{get;}
公共对象调用(对象实例,参数对象[]参数)
{
返回委托(实例、参数);
}
}
Profile以找到符合您期望的解决方案: .Net Framework提供了许多方法来动态调用方法。然而,它们的性能并不相同,也不容易使用

CreateDelegate可能就是您要找的 在.Net Framework的最新版本中,CreateDelegate比MethodInfo调用快了50倍:

// The following should be done once since this does some reflection
var method = typeof (...).GetMethod("ReadGeneric");
// Here we create a Func that targets the instance of type which has the 
// ReadGeneric method
var func = (Func<Tin, Tout[]>)_method.CreateDelegate(typeof(Func<Tin, Tout[]>), target);

// Func will be 50x faster than MethodInfo.Invoke
// use func as a standard Func like 
// var tout = func(index);
//以下操作应该执行一次,因为这会进行一些反射
var method=typeof(…).GetMethod(“ReadGeneric”);
//在这里,我们创建一个Func,它以具有
//ReadGeneric方法
var func=(func)u method.CreateDelegate(typeof(func),target);
//Func将比MethodInfo.Invoke快50倍
//将func用作标准func-like
//var tout=func(指数);

选中此项可查看缓存委托的不同方法调用上的基准测试。同样缓慢的是装箱和拆箱;你不应该把
(Tout[])
放在一个循环中。我想编译器可能会对此进行优化,但常识是将所有可能的内容都保留在长循环之外。装箱/取消装箱我注意到,如果在具有数百万个元素的紧循环中执行此操作,将导致大约10倍的性能损失!因此,是的,如果可以一般地键入上面的内容,那么一定要这样做。这个答案对我自己的问题帮助很大:将
Compile
调用开销放在一边,在“直接”方法调用之上还有以下开销:
arguments
数组分配/收集的成本,参数转换的成本(两次),实例转换的成本,以及多个附加方法调用的成本(
FastMethodInfo.Invoke
Delegate.Invoke
,以及潜在的
VoidDelegate.Invoke
——不过,如果允许,
FastMethodInfo.Invoke
可能由编译器内联)。这里的内容看起来像是
MethodInfo.Invoke
的一个很好的替代品,但是“几乎和直接调用一样快”这句话很容易引起误解。我不同意。我给出了完全正确的想法或印象,尤其是与其他方法相比。快速制作一个概念验证项目,自己看看。我想你想说的是,我不精确或不彻底,但我甚至会反对这种批评,因为OP的问题是:“我如何加快速度?”哦,我决不会批评代码的质量,因为它满足了问题中提出的要求。如果你说“它比反射快得多”,我不会争辩,因为它是。如果你说“在某些情况下,它几乎和直接呼叫一样快”,我甚至不会争辩,因为它是。但是我可以想到一些可行的用例,其中FastMethodInfo比直接方法调用慢很多倍,这导致我强烈反对“几乎和直接调用一样快”部分的措辞。我在.net 4.7下测试了表达式解决方案,它运行得也一样快