在C#4.0 b1中,dynamic的出现得到了很多人的关注,该关键字的出现能大幅改进某些情况下的开发效率,但是,通过大家对比测试,该方式同时也会带来很大的性能损失,方法的执行不过是对反射执行的封装,远不如使用Emit或者Expression编译委托来得快.但是在最新的C# 4.0 beta2中,dynamic的实现的得到了很大的改变.
首先使用测试程序对比下几种常见的方法调用.程序分别将对直接调用,传统反射调用,dynamic调用,expression调用,emit调用速度进行简单的对比,该对比程序只调用一个空方法,纯测试方法调用的耗时,如果测试有什么问题,欢迎提出.
下面给出测试主程序的源代码(完整代码见文后附带源码):
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace DynamicTest {
class Program {
static void Main(string[] args) {
TestClass staticInstanse = new TestClass();
dynamic dynamicInstanse = staticInstanse;
int num = 100000; //次数
var expressionFun = staticInstanse.GetExpressionCallFunction("TestMethod");
var emitFun = staticInstanse.GetEmitCallFunction("TestMethod");
var method = staticInstanse.GetType().GetMethod("TestMethod");
Console.WriteLine("开始对比测试(连续执行{0}次空方法):", num);
Stopwatch watch = new Stopwatch();
//测试直接调用
watch.Start();
for (int i = 0; i < num; i++) {
staticInstanse.TestMethod();
}
watch.Stop();
Console.WriteLine("直接调用耗时:{0} ms", watch.Elapsed.TotalMilliseconds);
//测试反射调用
watch.Reset();
watch.Start();
for (int i = 0; i < num; i++) {
method.Invoke(staticInstanse, null);
}
watch.Stop();
Console.WriteLine("使用反射调用耗时:{0} ms", watch.Elapsed.TotalMilliseconds);
//测试包含首次调用的dynamic调用
watch.Reset();
watch.Start();
for (int i = 0; i < num; i++) {
dynamicInstanse.TestMethod();
}
watch.Stop();
Console.WriteLine("使用包含首次调用的dynamic调用耗时:{0} ms", watch.Elapsed.TotalMilliseconds);
//测试出去首次调用后的dynamic调用
watch.Reset();
watch.Start();
for (int i = 0; i < num; i++) {
dynamicInstanse.TestMethod();
}
watch.Stop();
Console.WriteLine("使用去掉首次调用的dynamic调用耗时:{0} ms", watch.Elapsed.TotalMilliseconds);
//测试expression调用
watch.Reset();
watch.Start();
for (int i = 0; i < num; i++) {
expressionFun(staticInstanse);
}
watch.Stop();
Console.WriteLine("使用Expression调用耗时:{0} ms", watch.Elapsed.TotalMilliseconds);
//测试emit调用
watch.Reset();
watch.Start();
for (int i = 0; i < num; i++) {
emitFun(staticInstanse);
}
watch.Stop();
Console.WriteLine("使用Emit调用耗时:{0} ms", watch.Elapsed.TotalMilliseconds);
}
}
}
通过测试,最后得到的结果如下(测试结果进代表本人机器测试结果,其他机器可能会有不同,但是相信相对的的速度对比值应该一致):
通过测试可以得到以下的大致结果:
直接调用最快
使用emit调用其次,大概是直接调用的10倍左右
Expression调用再次,然后是dynamic调用,不过很明显,dynamic的第一次调用速度比较慢,去掉第一次的执行之后,dynamic已经和expression调用的时间相差不大,当然最慢的是传统反射调用,远远低于其他的调用方式.
在此可以看出,dynamic的实现相比b1中得到了极大的改善,已经能满足绝大多数使用情况.
我们可以通过Reflector来查看目前dynamic的实现方式:
很明显,dynamic不再是传统的反射调用,个人猜测也是采用通过某种方式编译成委托的方式进行调用,所以能使得性能得到了极大的提高.
测试程序完整源代码下载:/Files/leven/DynamicTest.rar