我已经为一种语言实现了编译器和虚拟机。实现在C#中,基于堆栈的VM使用反射在一组内置函数上进行函数调用。
许多代码只涉及推送和弹出堆栈值,但主力是函数调用。目前,函数调用的实现如下所示:
var calli = gencode[pc++] as CallInfo;
var calla = PopStackList(calli.NumArgs).ToArray();
var ret = calli.MethodInfo.Invoke(instance, calla);
if (ret != null) PushStack(ret);
传递和返回的所有数据项都是使用自定义类型系统的对象(不使用本机类型)。澄清:这是一个实例方法,而不是静态方法。
性能测试表明此MethodInfo.Invoke
非常慢。问题是如何以尽可能高的速度进行函数调用,可能是通过在编译器中做更多的准备工作并生成更好的代码。
作为对建议的回应,一种可能性是创建一个代表。不幸的是,据我所知,委托必须绑定到类的特定实例或静态方法,并在创建实例后创建委托而忽略了目的。
我看到投票结束,但在我看来,这个问题并不广泛。编译器应该如何在虚拟机中的实例方法上实现函数调用以获得最佳性能,至少比MethodInfo.Invoke()
快?
答案 0 :(得分:2)
好吧,如果您确定您的主要问题是MethodInfo.Invoke ...
使用System.Linq.Expressions(Expression.Call,Expression.Parameter)中的东西创建一个调用MethodInfo方法的表达式,传递参数为instance + arguments。
将该表达式编译为Action<tInstance, tArgs[]>
(不知道您的类型)。
在CallInfo类实例中缓存该Action。
根据需要调用该操作。
答案 1 :(得分:0)
如何将MethodInfo.Invoke转换为委托:
通常,当您使用反射调用方法时,您将调用MethodInfo.Invoke。不幸的是,事实证明这很慢。如果知道编译时方法的签名,则可以使用Delegate.CreateDelegate(Type,object,MethodInfo)将方法转换为具有该签名的委托。您只需传入要创建实例的委托类型,调用的目标(即调用该方法的对象)以及您要调用的方法。如果有此调用的通用版本以避免转换结果,那将是很好的选择,但不要紧。这是一个演示其工作原理的完整示例:
using System;
using System.Reflection;
public class Test
{
static void Main()
{
MethodInfo method = (string).GetMethod(“IndexOf”, new Type[]{typeof(char)});
Func<char, int> converted = (Func<char, int>)
Delegate.CreateDelegate(typeof(Func<char, int>), “Hello”, method);
Console.WriteLine(converted(‘l’));
Console.WriteLine(converted(‘o’));
Console.WriteLine(converted(‘x’));
}
}
这将打印出2、4和-1;直接调用“ Hello” .IndexOf(...)会得到的结果。现在让我们看看速度差异是什么...
无论是直接方法调用MethodInfo.Invoke还是委托,我们都对从主调用代码到被调用方法所花费的时间最感兴趣。为了使IndexOf本身花费尽可能少的时间,我通过传入“ H”对其进行了测试,以便立即返回0。正常情况下,测试是粗糙且准备就绪,但结果如下:
调用类型秒表每次调用滴答 直接0.18 倒影120 委托0.20
复制自:https://blogs.msmvps.com/jonskeet/2008/08/09/making-reflection-fly-and-exploring-delegates/