zoukankan      html  css  js  c++  java
  • 使用动态方法提升性能——匿名委托与动态方法的性能比较测试

    在.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 仔细定义动态方法有可能获得非常可观的性能提升。

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

  • 相关阅读:
    fish shell version
    golang io.ReadFull
    Unity5 2D Animation
    unity3d vscode
    golang bufio.Scanner
    kafka知识点
    linux clone
    Oracle查询在哪些 存储过程/函数/触发器 等等中包含 指定字符串
    在Oracle中,使用简洁的函数(Function)实现字符串split分割效果
    在Spring中,使用ProxyFactory实现对Cglib代理对象的再次代理
  • 原文地址:https://www.cnblogs.com/haiq/p/2001492.html
Copyright © 2011-2022 走看看