最近写了几篇《c#扩展方法奇思妙用》的文章,一直只是讨论如何扩展、如何使用的问题,几乎没有涉及效率方面。
而大家的回复好多都在问效率如何、性能怎样,也引起了我对效率的关注,今天将初步测试的结果发出来,大家一起探讨一下。

以前没太写过性能测试的代码,上网找了一下,说可以用Stopwatch进行计时,比较准确。
Stopwatch使用比较简单,几个方法从名字上就知道用用途:Reset(计时清零)、Start(开始计时)、Stop(停止计时),属性ElapsedMilliseconds就是执行操作所用的毫秒数。

为了简化测试,让更多人看明白,我们这是对IsNullOrEmpty扩展进行测试,它只是简单调用string.IsNullOrEmpty静态方法。
但为了让我们的测试更有趣一些,我们再加上两个相同功能的方法,一个是IsNullOrEmpty的手工实现版,称为手工方法,另外一个用lambda表达式写的。
一共是如下三个方法与string.IsNullOrEmpty(称为“原方法”)比较:

 1         //扩展方法
 2         public static bool IsNullOrEmpty1(this string s)
 3         {
 4             return string.IsNullOrEmpty(s);
 5         }
 6         //手工方法
 7         public static bool IsNullOrEmpty2(string s)
 8         {
 9             return s == null || s == string.Empty;
10         }
11         //lambda方法
12         public static Func<string, bool> IsNullOrEmpty3 = s => string.IsNullOrEmpty(s);
我们在函数名后面添加上一个数字,将它们区分开,以避免相互混淆。

为了测试公正,尽量消除测试中的误差,我们采用一个数组存放要测试的字符串。
这个数组中存放三种字符串,非Empty非Null、Empty、Null。随机存入,数量大致相同。生成算法如下:

 1         private static string[] GetTestStringArray(int count)
 2         {
 3             string[] result = new string[count];
 4             Random random = new Random();
 5 
 6             int r = 0;
 7             for (int i = 0; i < count; i++)
 8             {
 9                 r = random.Next(3);
10                 if (r == 0) result[i] = i.ToString();
11                 else if (r == 1) result[i] = string.Empty;
12                 else result[i] = null;
13             }
14             return result;
15         }
我们让这四个算法(前面三个算法+原来的静态算法)依次对数组中的每一项进行判断。
有一点要特别注意,对集合遍历也要耗时,我们要排除这段时间。
下面给出测试算法,写的不好,别见笑:
 1public static void Test()
 2{
 3    int count = 10000000;                                 //7个零
 4    string[] ss = GetTestStringArray(count);  //测试字符串Array
 5    bool b;
 6    string str;
 7
 8    long t = 0;    //基本循环时间
 9    long t0 = 0;    //原方法时间
10    long t1 = 0;    //扩展方法时间
11    long t2 = 0;    //手工方法时间
12    long t3 = 0;    //lambda时间
13
14    Stopwatch watch = new Stopwatch();
15    for (int i = 0; i < 10; i++)    //循环测试10次
16    {
17        watch.Reset(); watch.Start();
18        foreach (string s in ss) str = s;
19        watch.Stop();
20        Console.Write("基本循环:" + watch.ElapsedMilliseconds + "ms\t\t\t\t");
21        t += watch.ElapsedMilliseconds;
22
23        watch.Reset(); watch.Start();
24        foreach (string s in ss) { str = s; b = string.IsNullOrEmpty(str); }
25        watch.Stop();
26        Console.Write("原方法:" + watch.ElapsedMilliseconds + "ms\t\t");
27        t0 += watch.ElapsedMilliseconds;
28
29        watch.Reset(); watch.Start();
30        foreach (string s in ss) { str = s; b = str.IsNullOrEmpty1(); }
31        watch.Stop();
32        Console.Write("扩展方法:" + watch.ElapsedMilliseconds + "ms\t\t");
33        t1 += watch.ElapsedMilliseconds;
34
35        watch.Reset(); watch.Start();
36        foreach (string s in ss) { str = s; b = IsNullOrEmpty2(str); }
37        watch.Stop();
38        Console.Write("手工方法:" + watch.ElapsedMilliseconds + "ms\t\t");
39        t2 += watch.ElapsedMilliseconds;
40
41        watch.Reset(); watch.Start();
42        foreach (string s in ss) { str = s; b = IsNullOrEmpty3(str); }
43        watch.Stop();
44        Console.Write("lambda方法:" + watch.ElapsedMilliseconds + "ms\t\t");
45        t3 += watch.ElapsedMilliseconds;
46
47        Console.WriteLine();
48    }
49
50    Console.WriteLine();
51
52    Console.WriteLine(string.Format("扩展方法\t / 原方法\t = {0:f2}", (t1 - t) * 1.0 / (t0 - t)));
53    Console.WriteLine(string.Format("手工方法\t / 原方法\t = {0:f2}", (t2 - t) * 1.0 / (t0 - t)));
54    Console.WriteLine(string.Format("lambda方法\t / 原方法\t = {0:f2}", (t3 - t) * 1.0 / (t0 - t)));
55}
56
想重构一下,考虑了几种办法,不太好,怕重构后大家看起来更费力。

Test中的4个小段代码很相似,分别用来测量4个算法的用时。

1     foreach (string s in ss) str = s;

上面这句代码是基本循环,后面三组代码都在它基础上加入相应操作。
Test()不复杂,就是太啰嗦,大家都看得明白。

先在Debug模式下执行测试:

后面三个方法效率也太低了吧!!且一放,再看Release模式:

比前面效率提高了一些。最后是把Release模式下生成的程序,放在命令行中执行:

说明一:项目的输出类型必需是“控制台应用程序”才能在控制台中输出。
说明二:控制台的宽度比较小,我删除了Test()中输出中的几个制表符等,才让它输入不换行。
说明三:本处执行的是Release模式生成的程序,而不是Debug模式生成的程序。

Debug和Release测试是在VS2008宿主中进行的,最后控制台测试才是真正的实际运行环境,我们测试结果以控制台测试结果为准
之所以将前面两个贴出来,是告诉大家在vs中调试测试的结果是相当不准确的

我们来分析下测试的结果吧:
1.扩展方法的效率是相当高的,与原方法只有百分之几(多运行几次,可能是1、3、4甚至0,还有一次是-2,即比值为0.98)的性能损失。
2.手工方法效率最低,低得出乎大多数人的意料。
3.lambda会带来“可观”的性能损失

如果考虑性能:可以使用扩展方法,但扩展方法内部不要使用lambda表达式,其内部尽量使用常规代码。
(其实扩展方法内部代码简洁与否无所谓,毕竟扩展方法是一种封装,可以将内部复杂的操作隐藏起来并以一个简单的扩展方法提供给调用者)
如果考虑性能:少用lambda,多用原生方法。

感觉:这次测试的结果令我倍感意外,确实没想到扩展方法的效率如此之高(看来我的扩展想法有市场了)!
期望:本人是“粗人”,很不细心,大家如果发现上面测试中有错误,请马上告知我,谢谢!
打算:对一个扩展方法的测试说服力不够,以后会再做一些相关测试工作。
感慨:效率的高低不是眼睛看看、脑子想想能断定的,而必需采用科学的测试方法才可以给出结论
(讨论,如果本文只给出在debug及release下的测试结果,会是怎样的呢?)

本人系列文章《c#扩展方法奇思妙用》,敬请关注!

转载于:https://www.cnblogs.com/China-Dragon/archive/2010/05/12/1733503.html

c#扩展方法奇思妙用性能篇一:扩展方法性能初测相关推荐

  1. c#扩展方法奇思妙用高级篇七:“树”通用遍历器

    我的上一篇随笔<c#扩展方法奇思妙用高级篇六:WinForm 控件选择器>中给出了一个WinForm的选择器,其实质就是一个"树"的遍历器,但这个遍历局限于WinFor ...

  2. c# 扩展方法奇思妙用基础篇八:Distinct 扩展(转载)

    转载地址:http://www.cnblogs.com/ldp615/archive/2011/08/01/distinct-entension.html 刚看了篇文章 <Linq的Distin ...

  3. c#扩展方法奇思妙用高级篇四:对扩展进行分组管理

    从系列文章开篇到现在,已经实现的很多扩展了,但过多的扩展会给我们带来很多麻烦,试看下图: 面对这么多"泛滥"的扩展,很多人都会感到很别扭,的确有种"喧宾夺主"的 ...

  4. c# 扩展方法奇思妙用变态篇四:string 的翻身革命

    string是各种编程语言中最基础的数据类型,长期以来受尽其它类的压迫,经常被肢解(Substring.Split).蹂躏(Join)... 而现在string要"翻身闹革命"了, ...

  5. c#扩展方法奇思妙用变态篇四:string 的翻身革命

    string是各种编程语言中最基础的数据类型,长期以来受尽其它类的压迫,经常被肢解(Substring.Split).蹂躏(Join)... 而现在string要"翻身闹革命"了, ...

  6. c# 扩展方法奇思妙用高级篇一:改进 Scottgu 的 In 扩展

    先看下ScottGu对In的扩展: 调用示例1: 调用示例2: 原文地址:New "Orcas" Language Feature: Extension Methods 很多介绍扩 ...

  7. c# 扩展方法奇思妙用变态篇一:由 Fibonacci 数列引出 “委托扩展” 及 “递推递归委托”...

    先回顾一个数列的概念:按一定次序排列的一列 数 称为数列...(请参见百度百科:数列) 几个简单的数列:       1, 1, 1, 1, 1, 1, 1...                //数 ...

  8. c#扩展方法奇思妙用高级篇八:Type类扩展

    Type 类提供了大量的属性和方法,但在一些基础性开发工作中,Type类功能还有些欠缺,尤其上在处理泛型类型时,如可空类型和泛型集合类型.下面的类就针对这些地方进行扩展.  1     public  ...

  9. C#扩展方法奇思妙用高级篇一:改进 Scottgu 的 In 扩展

    先看下ScottGu对In的扩展: 调用示例1: 调用示例2: 原文地址:New "Orcas" Language Feature: Extension Methods 很多介绍扩 ...

  10. c# 扩展方法奇思妙用高级篇五:ToString(string format) 扩展

    在.Net中,System.Object.ToString()是用得最多的方法之一,ToString()方法在Object类中被定义为virtual,Object类给了它一个默认实现: 1     p ...

最新文章

  1. 从马尔科夫链到吉布斯采样与PageRank
  2. ecshop调用指定分类(包含子分类)下所有产品的评论信息
  3. 神策数据通过中国信通院 SDK 安全评测
  4. net与树莓派的情缘-安装与卸载MySql(五)
  5. vs工程 更换opencv版本_Windows下opencv源代码转化和编译
  6. windows启动管理器_必备的9个Windows设置技巧,可以将Windows 10的性能大幅提高
  7. Verilog奇偶校验_zt
  8. Windows server 2008计划任务(批处理命令)不执行
  9. SQL2005下载版本之区别
  10. 体系结构:Cache Coherence
  11. win7电脑如何提升开机速度?
  12. OSChina 周一乱弹 ——我们不应歧视任何语言,她们都是萌娘!(有图有真相)...
  13. RFE -- 用户活跃度模型
  14. 点阵发光管怎么用C语言编程,LED点阵经验各种点阵驱动方法讲解
  15. 惠普HP Ink Tank 118 打印机驱动
  16. 广东技术师范大学一行来访虹科并进行“见习-实习-就业一体化基地 ”挂牌仪式
  17. IE 零日漏洞风险评估
  18. Swift学习之--TableView的基本使用
  19. 流星雨html5,HTML5-流星雨
  20. 单片机实现温度传感器

热门文章

  1. POJ1459-Power Network
  2. flex pv3d 有用公式
  3. python os.listdir列举当前文件夹下所有文件及文件夹
  4. 在Windows下如何安装Tomcat服务器搭建
  5. NY : 括号匹配问题
  6. elif else if oracle_关于Oracle if elsif else
  7. android IO流_Flutter实战经验(十):打包和发布到 Android 平台
  8. MATLAB上的GPU加速计算——学习笔记
  9. 七点人脸姿态估计_Github开源库简单配置即可上线的3D人脸检测工具箱
  10. linux ub查看ftp安装,Linux Ubuntu 18.04 安装 FTP服务