web-dev-qa-db-ja.com

MethodInfo.Invokeパフォーマンスの問題

ファイルとの間でデータの読み取りと書き込みを行っています。ファイル内のデータは、float、double、intなどです。タイプは実行時までわかりません。ファイルに保存されているデータ型を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コロサル量がわかります。

これをスピードアップするにはどうすればよいですか。

17
Basil Furdas

はい、これは、リフレクションAPIが直接メソッド呼び出しよりも数千倍遅いという事実によるものです。ただし、これを回避するための興味深い手法がいくつかあります。 デリゲートを使用してリフレクションをキャッシュする に関するJonSkeetの記事を確認してください。

静的なセットアップコストがかかりますが、一度それを行うと、デリゲートを繰り返し呼び出す時間は仮想メソッド呼び出しと同等になります。

同じことを達成するために、いくつかの 事前にパッケージ化されたフレームワーク もあります。

24
Dr. ABT

これは、直接電話とほぼ同じ速さで、あなたのために何でもします。

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);
    }
}
7
Daniel Henry

あなたの期待に一致する解決策を見つけるためのプロファイル:

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

これを確認してください 私の投稿 さまざまなメソッド呼び出しのベンチマークを確認してください

0
Fab