当前位置: 首页 > 编程日记 > 正文

.Net 中字符串性能

Introduction

你在代码中处理字符串的方法可能会对性能产生令人吃惊的影响。在本文中,我需要考虑两个由于使用字符串而产生的问题:临时字符串变量的使用和字符串连接。

Background

每个项目都有需要你为其考虑编码标准的时候。使用 FxCop 是一个好的开始。我最喜爱的一组 FxCop 规则是“性能”那组。

于是,我就用 FxCop 来检查我的项目并发现一系列的字符串问题。我必须承认一件事:我经常遇到与 C# 的不可变(immutable)的字符串有关的问题。当我看到 myString.ToUpper() 时,我经常都会忘记它并不是改变 myString 的内容而是返回一整个全新的字符串(这是由于 C# 中字符串是不可变的)。

我对代码进行一番修正以便去掉 FxCop 的警告,接着我就发现代码的确比之前快了。我决定开展调查,而最终我会写出上面那些测试的代码的。

Using the code

测试的代码很简单。一个控制台程序调用四个测试方法,其中每个方法执行一种字符串处理例程 1000 次(整个执行时间已经足够长以便看出其中的性能差别了)。

这四个测试方法被分成两组,每组两个。第一组比较两个方法,它们用于非大小写敏感(case-insensitive)的字符串比较。

String Comparison and Temporary String Creation

第一个测试例程是一个蹩脚的非大小写敏感的字符串比较。用于比较的例程的代码是:

static bool BadCompare(string stringA, string stringB)
{
    
return (stringA.ToUpper() == stringB.ToUpper());
}

对于这段代码,FxCop 给出如下的建议:

"StringCompareTest.BadCompare(String, String):Boolean calls String.op_Equality(String, String):Boolean after converting 'stack1', a local, to upper or lowercase. If possible, eliminate the string creation and call the overload of String.Compare that performs a case-insensitive comparison."

这项建议的意思是每次对 ToUpper() 的调用都会创造一个临时字符串,而这个临时字符串是由垃圾收集器来创建和管理的。这需要额外的时间和使用更多的内存。 String.Compare 方法(相对来说)更加高效。

第二个测试例程使用 String.Compare :

static bool GoodCompare( string stringA, string stringB)
{
    
return (string.Compare(stringA, stringB, true , System.Globalization.CultureInfo.CurrentCulture) == 0);
}

这个方法防止多余的临时字符串的创建。

根据 nprof 的分析结果 , GoodCompare 的执行时间只占代码总执行时间的 1.69%, 而 BadCompare 的执行时间则占总执行时间的 5.50% 。

因此 String.Compare 方法比 ToUpper 方法快了三倍有余。如果你的代码您执行了很多字符串的比较(尤其是在循环里面执行),使用 String.Compare 能(使你的代码在性能上)有较大的改善。

String Concatenation inside a loop

最后那对测试例程设想字符串的连接是在一个循环里面进行的。

“蹩脚”的测试例程的代码如下:

static string BadConcatenate(string [] items)
{
    
string strRet = string .Empty;

    
foreach (string item in items)
    
{
        strRet += item;
    }


    
return strRet;
}

当 FxCop 看到这段代码,它就会很愤怒,甚至用红色标记这项被破的规条! FxCop 这样说道:

"Change StringCompareTest.BadConcatenate(String[]):String to use StringBuilder instead of String.Concat or +="

“优良”的测试例程的代码如下:

static string GoodConcatenate(string [] items)
{
    System.Text.StringBuilder builder = 
new System.Text.StringBuilder();

    
foreach (string item in items)
    
{
        builder.Append(item);
    }


    
return builder.ToString();
}

这段代码几乎被用作展示 System.Text.StringBuilder 的用法的首选例子。蹩脚的代码的问题是创建了过多的临时字符串。由于字符串的不可变特性,连接操作符(+=)实际上用原来那两个字符串来创建一个新的字符串,然后把原来的字符串实例指向这个新的字符串。

但是,依据 nprof 来研究代码性能,我们发现运行 BadConcatenate 只需总执行时间的 5.67% ,而 GoodConcatenate 则是 22.09% 。也就是说:

使用 StringBuilder 耗费的时间几乎是简单的字符串连接的四倍!

为什么呢?

部分原因在于这个测试的设计——连接例程仅仅连接了十个简短的字符串。 StringBuilder 是一个比简单的不可变的字符串类更复杂的类,因此创建一个 StringBuilder 比起进行十个简单的字符串连接在性能上是昂贵很多的。

我重复地做不同数目的字符串连接的测试,并且发现以下结果:

Chart of concatenation method effect on relative performance.

注意:这里所显示的数值是测试例程的执行时间占总执行时间的百分比(%)。 GoodConcatenate 实际上并没有快很多,但与 BadConcatenate 比却相对地快了。

因此, StringBuilder 通常只有在你要连接的字符串数目超过 600 时才会显示出真正的性能优势。

当然,另外一个使用 StringBuilder 的原因就是是内存的分配。使用 CLRProfiler 生成下面这个连接 100 个简单字符串时内存使用情况的时序图:

Memory usage timeline.

标记为“A”的区域显示了 BadConcatenate 在内存分配和释放上的效果。被分配内存的最大值迅速增加,并伴有大数量的垃圾收集的发生(该区域有大约 215 次垃圾收集)。

紧随在“A”区后面的区域显示了 GoodConcatenate 的内存轮廓。被分配内存的最大值增量较少,且伴随着非常少的垃圾收集(该区域有大致 60 次垃圾收集)。

所以在某些情况下使用 StringBuilder 类并不会(使你的代码运行得)更快 , 但它对垃圾收集器是友好的。

Conclusions

使用 String.Compare 方法进行非大小写敏感的字符串比较。这样更快。而且代码优雅和简单。

仅当你在一个循环里进行超过 600 次的字符串连接时,使用 StringBuilder 来获得更好的速度。这里需要提醒的是,你所处理的字符串的长度也会影响最终的速度,同样会影响垃圾收集器的效果,所以你应该根据你实际的代码具体问题具体分析。

Points of Interest

令我惊讶的是,在真实世界运用正确的代码字符串操作方法的还是很不同(虽然我们已在当前的项目中进行了很多字符串的比较和连接)。

FxCop 的性能规则是发现潜在低性能代码的好起点,并能指导你进行一些简易修正来改善代码性能。这里所讨论的两个问题都被 FxCop 标记为“NON-BREAKING”,这是指改动不应破坏依赖于被改动代码的代码。认为为改善性能而做的改动都是“NON-BREAKING”则是没头脑的想法。

Further Considerations By Allen Lee

使用 StringBuilder 来处理字符串的连接应该是绝大多数 .NET 开发人员的共识了。但你有否曾经怀疑过这一经验原则的适用性是否真如想象中那么广泛呢?读过本文后,或许你已经意识到这是个适度的问题。对小规模的字符串连接使用 StringBuilder 所带来的改善根本不足以抵偿因 StringBuilder 本身的复杂性所产生的开销;只有当连接规模达到临界规模,两者才能相互抵偿从而达至平衡。

对于实际的代码,一个可供使用的临界规模值可能是必需的,尤其是在受限系统上进行开发。你可能因为对影响临界规模的因素有所了解而怀疑作者在这里所给出的数字。或许本文用于测试的设计显得有点简单以至于未必能使更多的人信服,但你的确透过本文了解到 StringBuilder 并不是任何情况都适用的。由于影响临界规模的因素总有可能发生变化,你不可能找到一个对任何情况都适用的确定的临界规模值。你应该为你的代码量身订造一个,并随时做好调整的准备(因为变化总是存在的),只要你真的那么在意这方面的性能影响。作为一个开始,你可以以作者在本文所提到的那个数字作为一个参照基础,并就具体的情况进行微调,直到你满意为止。

相关文章:

Lambda表达式可以被转换为委托类型

void Main() { //向Users类中增加两人; List<Users> usernew List<Users>{ new Users{ID1,Name"Jalen",Age23}, new Users{ID12,Name"Administrator",Age32}, }; //接下来就是利用Linq提供的新的方法来进行相关操作; var userslistuser.Wher…

人工干预如何提高模型性能?看这文就够了!

作者 | Preetam Joshi译者 | 吴家帆出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;有一些行业对误报非常敏感&#xff0c;如金融行业&#xff0c;在对信用卡欺诈检测时&#xff0c;如果检测系统将用户的行为错误地分类为欺诈&#xff0c;这将对该金融机构的声誉产生…

一种无需留坑为页面动态添加View方案

在Activity或Fragment页面动态添加View&#xff0c;有其应用场景&#xff0c;比如配合运营在首页动态插入H5活动页&#xff08;如下图手淘的雪花例示[1]&#xff09;,在页面头部插入通知View等。本文结合ActivityLifecycleCallbacks[2]及DecorView使用&#xff0c;为类似需求提…

边缘加速创新和AI应用,Xilinx推出Kria自适应系统模块产品组合

为了帮助开发者更容易使用FPGA和SoC的功能&#xff0c;赛灵思在开发工具上做了不少的投入&#xff0c;自适应系统模块&#xff08;SOM&#xff09;产品组合就是其中之一。 近日&#xff0c;赛灵思宣布推出Kria™自适应系统模块&#xff08; SOM &#xff09;产品组合&#xff…

windows计算器

using System; using System.Drawing; using System.Windows; using System.Windows.Forms; using System.Collections; using System.ComponentModel; using System.Data; namespace comput{ /// <summary> /// 这是一个计算器的简单实现。 /// </summary&…

哈夫曼树的构造

[转载于网易博客&#xff0c;具体地址不详] 构造哈夫曼树的过程是这样的 一、构成初始集合 对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F{T1,T2,T3,...,Ti,...,Tn}&#xff0c;其中每棵二叉树Ti中只有一个权值为Wi的根结点&#xff0c;它的左右子树均为空…

物联网时代全面降临

从智能建筑到零售&#xff0c;英特尔物联网解决方案可以说是华丽丽地惊艳着大家的大脑和眼球&#xff0c;一切的不可能似乎都在朝着可能的方向努力着。在2015 MWC上&#xff0c;英特尔再次用各种神奇的物联网设备告诉大家&#xff1a;物联网时代已经来临。 “半边天”的力量&am…

Linux C++/Java/Web/OC Socket网络编程

一&#xff0c;Linux C Socket网络编程 1.什么是TCP/IP、UDP&#xff1f; TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;即传输控制协议/网间协议&#xff0c;是一个工业标准的协议集&#xff0c;它是为广域网&#xff08;WANs&#xff09;设…

ASP.NET抓取其他网页代码

在.Net 平台下&#xff0c;创建一个ASP.Net的程序 1、引用两个NAMESPACE using System.Text //因为用了Encoding类 using System.Net //因为用了WebClient 类 2、整个程序用了三个控件 txtUrl //输入你要获取的网页地址 TEXTBOX控件 txtBody //得到你要获取的网…

特斯拉遇上 CPU:程序员的心思你别猜

作者 | 码农的荒岛求生来源 | 码农的荒岛求生图源 | 视觉中国18世纪流水线的诞生带来了制造技术的变革&#xff0c;人类当今拥有琳琅满目物美价廉的商品和流水线技术的发明密不可分&#xff0c;因此当你喝着可乐、吹着空调、坐在特斯拉里拿着智能手机刷这篇文章时需要感谢流水线…

《算法技术手册》一2.4.6 二次方的算法性能

2.4.6 二次方的算法性能 现在考虑一个类似的问题&#xff1a;两个n位的整数相乘。例2-4展示了使用小学课堂上学过的算法实现的乘法运算&#xff0c;其中n位数字的表示方法与之前的加法一样。 例2-4&#xff1a;mult乘法的Java实现 public static void mult (int[] n1, int[] n2…

如何使用 OpenCV 实现图像均衡?

来源 | 小白视觉志头图 | 下载于视觉中国我们已经练习了很多图像处理——操作图像&#xff08;精确地说是图像矩阵&#xff09;。为此&#xff0c;我们探索了图像的均衡方法&#xff0c;以便在一定程度上增强对比度&#xff0c;以使被处理的图像看起来比原始图像更好&#xff0…

《中国人工智能学会通讯》——1.42 理解情感

1.42 理解情感 安德鲁摩尔认为&#xff0c;人工智能能“感受”人类情感是人工智能研究领域最重要、也最先进的一个方向。扬波利斯基认为&#xff0c;计算机能够理解语言的能力最终会向人和计算机“无缝沟通”的方向发展。 越来越精准的图像、声音和面部识别系统能让计算机更好探…

matlab中help所有函数功能的英文翻译

doc funname 在帮助浏览器中打开帮助文档help funname 在命令窗口打开帮助文档helpbrowser 直接打开帮助浏览器lookfor funname 搜索某个关键字相关函数demo 打开视频教程 转http://blog.renren.com/share/239121107/690877048 里面有些不全的&#xff0c;自己用到的已添加…

C# 静态构造函数

&#xff08;1&#xff09;用于对静态字段、只读字段等的初始化。 &#xff08;2&#xff09;添加static关键字&#xff0c;不能添加访问修饰符&#xff0c;因为静态构造函数都是私有的。 &#xff08;3&#xff09;类的静态构造函数在给定应用程序域中…

破解数据流通痛点,华控清交的隐私计算之道

从无序中寻找踪迹&#xff0c;从眼前事探索未来。 正值 IT 黄金十年新开端&#xff0c; CSDN 欲以中立技术社区专业、客观的角度&#xff0c;深度探讨中国前沿 IT 技术演进&#xff0c;现在推出年度重磅企划栏目——「拟合」&#xff0c;通过对话企业高管大咖&#xff0c;跟踪报…

mac系统添加VSCode到右键菜单(转)

转自&#xff1a;https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001470969077294a6455fc9cd1f48b69f82cd05e7fa9b40000 在Mac系统上&#xff0c;Finder选中一个目录&#xff0c;右键菜单并没有“通过Code打开”这个操作。不过我们可以…

在 C# 中通过 P/Invoke 调用Win32 DLL

&#xff0c;.NET Framework 1.0 或 1.1 版类库中存在任何 Windows 所没有的功能限制都不足为怪。毕竟&#xff0c;32 位的 Windows&#xff08;不管何种版本&#xff09;是一个成熟的操作系统&#xff0c;为广大客户服务了十多年。相比之下&#xff0c;.NET Framework 却是一个…

xp/2003开关3389指令

开启3389&#xff1a; echo offtitle 开启3389clsrem 开启3389reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 00000000 /f >nulecho.echo 提示你&#xff1a;3389已经开启 关闭3389&…

TIOBE 新榜单:Python 超越 Java 重回第二,Rust 崛起

作者 | 苏宓出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;TIOBE 官方最新发布了 5 月的编程语言榜单&#xff0c;不妨一起来看一下本月榜单中又有哪些最新的变化呢&#xff1f;Python 重回第二和 4 月相比&#xff0c;本月榜单的 TOP 10 中变化最大的非 Python 与…

Docker编排工具Fig介绍

本文讲的是Docker编排工具Fig介绍&#xff0c;【编者的话】Fig是一个基于Docker的用于快速搭建开发环境的工具&#xff0c;目前Fig团队已经加入Docker公司。Fig通过一个配置文件来管理多个Docker容器&#xff0c;非常适合组合使用多个容器进行开发的场景。Fig可以和Docker一起来…

java调用ffmpeg,mencoder进行视频转换,读取时长等

2019独角兽企业重金招聘Python工程师标准>>> 以前做的一个基于ffmpeg的视频格式转换的程序&#xff0c;现在抽空整理一下&#xff0c;很多地方都是从别的大神那借鉴的&#xff0c;只是把自己的觉得有用的&#xff0c;对别人有帮助的拿出来分享分享&#xff0c;下面是…

数字人民币实现可控匿名交易?产业升级离不开安全可信的“数字底座”

自央行进行数字人民币试点测试工作以来&#xff0c;人们讨论最多的可能是它的便捷性、匿名性。不过&#xff0c;它的意义远不止于人类个体层面。 作为一种面向未来的货币形式&#xff0c;在未来数字经济时代&#xff0c;央行数字人民币的普及无疑将加速全球资产数字化和身份数…

apache+tomcat 搭建负载均衡系统

apachetomcatmod_jk 搭建负载均衡系统。0.os系统采用centos6.8 x64 2.6.32-642.el6.x86_641.首先安装好jdk环境本次采用jdk-8u111-linux-x64.gz jdk和jre的安装目录要不同&#xff0c;否则的话lib目录下没有dt.jar 和tools.jar 要配置好环境变量如下 vi /etc/profile #ad…

从普本到北大:我的跨校跨专业考研经验

首先做一个我考研情况的简介。 经历了2013年考研的混战&#xff0c;据说是史上考研人数顶峰的年份&#xff0c;因为2014改革&#xff0c;不再有自费生之后&#xff0c;人民群众对于所谓学术硕士的需求量激减&#xff0c;继 而投奔价格费用相当&#xff0c;读书年份较少的专业硕…

C#中使用DirectX编程

我感觉声音的播放比较简单。我们从播放声音开始。为什么我这么觉得&#xff1f;我也不知道。这里是展示最最最最最简单的DirectX播放声音的例子&#xff0c;我尽量省略了无关的代码。最后的代码只有19行&#xff0c;够简单了吧&#xff1f; 准备工作&#xff1a;1.安装了Direc…

40+场面试,100%通过率,我想分享的14条经验

来源 | 陈同学在搬砖头图 | 下载于视觉中国大家好&#xff0c;我是陈同学&#xff0c;首先来一个简单的自我介绍和个人的经历分享。我的本科和硕士均就读于哈工大&#xff0c;在研究生期1年时间内自学操作系统、计算机网络、C、数据结构等&#xff0c;累计学习30本书、500博客文…

云端卫士架构师讲DDoS攻击的智能防御之道

DDoS即分布式拒绝服务攻击&#xff0c;这是一场关乎资源的较量&#xff0c;攻击者通过自己控制的大量僵尸主机&#xff0c;向目标设施&#xff08;服务器、运营商网络和基础架构等&#xff09;发起洪水猛兽般的流量型攻击&#xff0c;或是连绵不绝的应用型攻击。 如果将受害者比…

C#中方法参数的四种类型

C&#xff03;中方法的参数有四种类型&#xff1a;&#xff0d;值参数&#xff1a;不含任何修饰符。方法中的形参是实参的一份拷贝&#xff0c;形参的改变不会影响到内存中实参的的值&#xff0c;实参是安全的。&#xff0d;引用参数&#xff1a;以ref修饰符声明。传递的参数实…

赠书 | 算力时代,用 Python 来快速解决复杂问题

Python作为一种编程语言&#xff0c;拥有简洁、高效的表达能力。与此同时&#xff0c;Python语言环境中还配备各种软件库&#xff0c;即模块。结合实际问题&#xff0c;选择适当的模块&#xff0c;便可生成简单、快速、正确的程序。书中列举了一些数值计算的简单例题&#xff0…