19

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

代码序列是这样的。在已知的 Open 方法 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 的数量会很大。

我该如何加快速度。

4

3 回答 3

29

是的,这是因为反射 API 比直接方法调用慢数千倍。然而,有一些有趣的技术可以解决这个问题。查看 Jon Skeet 关于使用委托缓存反射的文章。

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

也有一些预打包的框架来实现同样的事情。

于 2012-04-25T10:44:09.767 回答
8

这将为您做任何事情,几乎与直接呼叫一样快。

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);
    }
}
于 2017-01-07T00:48:22.467 回答
0

配置文件以找到符合您期望的解决方案:

.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);

检查我的这篇文章以查看不同方法调用的基准

于 2017-04-20T03:00:39.207 回答