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

最近写了几篇《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<stringbool> 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()
 2ExpandedBlockStart.gifContractedBlock.gif{
 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次
16ExpandedSubBlockStart.gifContractedSubBlock.gif    {
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();
24ExpandedSubBlockStart.gifContractedSubBlock.gif        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();
30ExpandedSubBlockStart.gifContractedSubBlock.gif        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();
36ExpandedSubBlockStart.gifContractedSubBlock.gif        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();
42ExpandedSubBlockStart.gifContractedSubBlock.gif        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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/381040.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云服务器抢票咋用_[Mac] 阿里云服务器+分流抢票教程

一.前言临近过年, 抢春运的票成为了我们人生的头等大事, 我们都知道分流是最好用的抢票软件之一, 但是只支持windows, 又因为我工作的电脑是Mac所以不能安装, 是得想个办法才好, 最终决定了使用 阿里云服务器 分流 进行抢票, 是不是听起来很高级呢? 下面就跟着我们的上下文一…

Centos7忘记密码——转

应用场景 linux管理员忘记root密码&#xff0c;需要进行找回操作。 注意事项&#xff1a;本文基于centos7环境进行操作&#xff0c;由于centos的版本是有差异的&#xff0c;继续之前请确定好版本。 操作步骤 一、重启系统&#xff0c;在开机过程中&#xff0c;快速按下键盘上…

收集到的非常好的第三方控件

1. vs2010:增强xmal智能提示 http://visualstudiogallery.msdn.microsoft.com/en-us/1a67eee3-fdd1-4745-b290-09d649d07ee02. PowerCommands for Visual Studio 2008:http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectNamePowerCommands&Downl…

IDA

国庆礼物最强反编译工具 ida pro 7.0 破解补丁7.0sdk all sdk_utils 国内某团购群最新泄露的的版本 x86 arm x64 f5插件为最新版本 正版价值人民币3-5万 资源包含 ida pro 7.0 for mac ida pro 7.0 for windows IDA_Pro_7.0去局域网检测和兼容老版本idb补丁 2017/09/21 21…

hsql mybatis 表不存在_单元测试MyBatis与HSQL而不是Oracle

我想使用HSQL内存数据库对MyBatis持久层进行单元测试.真正的应用程序使用Oracle数据库.这工作正常,我们开始为id列添加自动递增的数字. Oracle需要使用序列来获取递增的数字,因此在Oracle数据库中创建了一个名为basis_seq的序列.在我的MyBatis mapperXML文件中我有这个&#xf…

AjaxToolKit学习笔记 之 ModalPopupExtender

1.控件功能描述 以模式窗口的方式弹出客户或服务器控件,以突出显示! 弹出的一般是DIV或PANEL. 2.控件属性描述 TargetControlID : 控制是否弹出的控件的ID. PopupDragHandleControlID : 允许拖拽的控件的ID. PopupControlID: 指定要弹出的控件的ID. BackgroundCssClass: 指定弹…

WIN10中DOCKER的安装与使用

WIN10中DOCKER的安装与使用 WIN10中DOCKER的安装与使用 1.docker的安装 环境准备 下载安装 2.docker的入门 开始使用 3.docker的常用配置 在PowerShell中设置 tab键自动补全&#xff08;其实用的都是cmd.exe&#xff09; Settings 4.用Dockerfile定义一个镜像 新建目录文件 构建…

单片机涡轮流量传感器_关于涡轮流量计传感器的维护保养

涡轮流量计是一种精密流量测量仪表&#xff0c;与相应的流量积算仪表配套可用于测量液体的流量和总量。广泛用于石油、化工、冶金、科研等领域的计量、控制系统。配备有卫生接头的涡轮流量计传感器可以应用于制药、食品等行业。 涡轮流量计传感器部分的维护保养要注意以下几点&…

孩子在华艺舞校的画画投稿-天女之梦

转载于:https://www.cnblogs.com/speeding/archive/2010/05/24/2623966.html

controller控制器怎么写_I2C控制器编程_框架

来源&#xff1a;百问网_嵌入式Linux wiki_jz2440 新1期视频维基教程 (视频文字版)作者&#xff1a;韦东山本文字数&#xff1a;3776&#xff0c;阅读时长&#xff1a;5分钟我们现在来讲I2C控制器怎么写&#xff0c;它是I2C程序中最核心的地方&#xff0c;我们要先构造几个结构…

关于数据库主键和外键

一、什么是主键、外键&#xff1a; 关系型数据库中的一条记录中有若干个属性&#xff0c;若其中某一个属性组(注意是组)能唯一标识一条记录&#xff0c;该属性组就可以成为一个主键 比如 学生表(学号&#xff0c;姓名&#xff0c;性别&#xff0c;班级) 其中每个学生的学号…

sql server charindex函数和patindex函数详解(转)

charindex和patindex函数常常用来在一段字符中搜索字符或字符串。假如被搜索的字符中包含有要搜索的字符&#xff0c;那么这两个函数返回一个非零的整数&#xff0c;这个整数是要搜索的字符在被搜索的字符中的开始位数。patindex函数支持使用通配符来进行搜索&#xff0c;然而c…

Win10最新批量激活

W269N-WFGWX-YVC9B-4J6C9-T83GX 先来说下使用激活码使用方法&#xff1a; 1、同时按下Win键X&#xff0c;然后选择命令提示符&#xff08;管理员&#xff09; 2、在命令提示符中依次输入&#xff1a; slmgr.vbs /upk &#xff08;此时弹出窗口显未“已成功卸载了产品密钥”…

dosbox 伪指令dd为什么会报错_什么是SQL函数?为什么使用SQL函数可能会带来问题?...

本文已收录GitHub&#xff0c;更有互联网大厂面试真题&#xff0c;面试攻略&#xff0c;高效学习资料等函数在计算机语言的使用中贯穿始终&#xff0c;在 SQL 中我们也可以使用函数对检索出来的数据进行函数操作&#xff0c;比如求某列数据的平均值&#xff0c;或者求字符串的长…

最新webshell大合集

收集与整理了各种webshell&#xff0c;以便在日后的项目中做Webshell检测训练。 https://github.com/tennc/webshell 各种webshell集合 https://github.com/ysrc/webshell-sample webshell样本 https://github.com/xl7dev/WebShell Webshell && Backdoor Collection…

JavaScript 参考教程(二)——转载

对象化编程 JavaScript 是使用“对象化编程”的&#xff0c;或者叫“面向对象编程”的。所谓“对象化编程”&#xff0c;意思是把 JavaScript 能涉及的范围划分成大大小小的对象&#xff0c;对象下面还继续划分对象直至非常详细为止&#xff0c;所有的编程都以对象为出发点&…

centos7 docker删除端口映射_容器Docker详解

概述基本概念Docker是一个开源的应用容器引擎&#xff0c;基于Go语言并遵从Apache2.0协议开源。Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的Linux机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&a…

深入解析结构化异常处理(SEH) - by Matt Pietrek

尽管以前写过一篇SEH相关的文章《关于SEH的简单总结》&#xff0c; 但那真的只是皮毛&#xff0c;一直对Windows异常处理的原理似懂非懂&#xff0c; 看了下面的文章 &#xff0c;一切都豁然开朗. 1997年文章&#xff0c;Windows技术的根一直没变&#xff1a;http://www.micr…

IE Mobie6清除浮动

看来已经有很多中window的毒了&#xff0c;平时操作系统是window&#xff0c;现在还好多人用window mobie&#xff0c;看来已经习惯了window的使用方式。做前端开发&#xff0c;有个IE 6已经够受的了&#xff0c;没想到半路杀出个IE Mobie。期待IE6被埋葬的一天。 做几个页面&a…

@requestbody 接受int参数_C++之指针作为函数参数

C 允许您传递指针给函数&#xff0c;只需要简单地声明函数参数为指针类型即可。下面的实例中&#xff0c;我们传递一个无符号的 long 型指针给函数&#xff0c;并在函数内改变这个值&#xff1a;#include <iostream> #include <ctime>using namespace std; void ge…