使用动态方法提升性能——匿名委托与动态方法的性能比较测试

在.NET中,应该说委托是非常平凡的一个概念,常常用于事件机制的实现,动态算法的定义等方面。它很常见,很简单,似乎没什么值得研究的。

但是,今天我对委托有了新发现,发现平凡的“委托”也可以有它不凡的一面。

我们知道,委托是一种引用方法的类型,可以通过命名方法创建委托的实例,也可以定义匿名的委托实例,例如:

1 // ComputeHandler 是一个执行算术计算的委托;
2   public delegate int ComputeHandler(int a1, int a2, int a3);
3
4 //定义匿名委托实现计算;
5   ComputeHandler anonymousHandler = delegate(int v1, int v2, int v3)
6 {
7 return v1 * v2 * v3;
8 };
9
10 //定义命名委托实现计算;
11 ComputeHandler namedHandler = new ComputeHandler(Compute);
12
13
14
15 //命名方法
16 private static int Compute(int v1, int v2, int v3)
17 {
18 return v1 * v2 * v3;
19 }

 但以上的这些都不新鲜,接下来的才是新鲜玩意:通过动态方法创建委托实例。

//使用动态方法创建委托实例;
private static ComputeHandler GetDynamicMethod()
{
DynamicMethod mth
= new DynamicMethod("_", typeof(int),
new Type[] { typeof(int), typeof(int), typeof(int) });
ILGenerator il
= mth.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Ret);
return (ComputeHandler) mth.CreateDelegate(typeof(ComputeHandler));
}

也许你会说,这只是定义的方式不同罢了,特别之处在哪儿呢?我告诉你,特别之处在于性能。

在这里,实现对 v1、v2、v3 三个整数进行相乘运算的调用有 4 种方式:1、匿名委托调用;2、命名委托调用;3、动态方法调用;4、直接对编译方法调用;

下面的测试用例用于比较这 4 种方式的性能表现:

int v1 = 2;
int v2 = 3;
int v3 = 8;

int testCount = 5;
int computeCount = 10000 * 10000;
int res = 0;



Console.WriteLine(
            //定义匿名委托实现计算;

ComputeHandler anonymousHandler
= GetAnonymousDelagate();
for (int t = 0; t < testCount; t++)
{
DateTime start
= DateTime.Now;
for (int i = 0; i < computeCount; i++)
{
res
= anonymousHandler(v1, v2, v3);
}
DateTime end
= DateTime.Now;
Console.Write(
"\t{0}\t", (end - start).TotalMilliseconds.ToString("0.00"));
}
Console.WriteLine(
"\r\n 计算结果:{0} ;", res);
Console.WriteLine();

Console.WriteLine(
"命名委托执行……");
Console.Write(
" 耗时(ms)");

//定义命名委托实现计算;
ComputeHandler namedHandler = GetAnonymousDelagate();
for (int t = 0; t < testCount; t++)
{
DateTime start
= DateTime.Now;
for (int i = 0; i < computeCount; i++)
{
res
= namedHandler(v1, v2, v3);
}
DateTime end
= DateTime.Now;
Console.Write(
"\t{0}\t", (end - start).TotalMilliseconds.ToString("0.00"));
}
Console.WriteLine(
"\r\n 计算结果:{0} ;", res);
Console.WriteLine();

Console.WriteLine(
"动态方法执行……");
Console.Write(
" 耗时(ms)");
//定义动态方法实现计算;
ComputeHandler dynamicHandler = GetDynamicMethod();
for (int t = 0; t < testCount; t++)
{
DateTime start
= DateTime.Now;
for (int i = 0; i < computeCount; i++)
{
res
= dynamicHandler(v1, v2, v3);
}
DateTime end
= DateTime.Now;
Console.Write(
"\t{0}\t", (end - start).TotalMilliseconds.ToString("0.00"));
}
Console.WriteLine(
"\r\n 计算结果:{0} ;", res);
Console.WriteLine();

Console.WriteLine(
"编译方法执行……");
Console.Write(
" 耗时(ms)");
for (int t = 0; t < testCount; t++)
{
DateTime start
= DateTime.Now;
for (int i = 0; i < computeCount; i++)
{
res
= Compute(v1, v2, v3);
}
DateTime end
= DateTime.Now;
Console.Write(
"\t{0}\t", (end - start).TotalMilliseconds.ToString("0.00"));
}
Console.WriteLine(
"\r\n 计算结果:{0} ;", res);
"匿名委托执行……");
Console.Write(
" 耗时(ms)");

测试结果如下:

1、匿名委托执行:2187.50 - 2218.75 ms ;

2、命名委托执行:2156.25 - 2187.50 ms ;

3、动态方法执行:1062.50 - 1093.75 ms ;

4、编译方法执行:1812.50 - 1843.75 ms ;

测试结果显示,此测试中动态方法的性能最高,其耗时只有其它方式的 50% 左右,比编译方法还快得多。

最差的是匿名委托,命名委托与之相差不大,编译方法比匿名方法提升约 16% 。

这一结果为我们设计高性能的 .NET 程序指出了一种方法:对于核心的、被大量频繁调用的算法通过 IL 仔细定义动态方法有可能获得非常可观的性能提升。

说明:由于本文所述案例的测试场景是在调试环境下,当时并未考虑到编译器优化的因素。当考虑编译器优化的因素后,本文所用的测试案例对于“使用动态方法提升性能”这一论点就难以支持,将在下篇文章中对此进行阐述。

原文地址:https://www.cnblogs.com/haiq/p/2001492.html