C#软件性能优化

1.    性能

衡量一个软件系统性能的常见指标有:响应时间、负载、资源使用率、并发数。在软件中有具体的提高性能需求时,我们需分析该系统性能的影响由哪些因素组成,再针对各部分进行性能优化。例如:我们在仪器设备软件中,从数据读写、算法计算、业务过程、通讯环节分析,根据需求的性能指标进行优化。

1.1      优化性能的原则

结构优化,减少数据交互的频次;

减小数据的交互量;

优化资源,减少创建对象的次数;

提高数据的读写速度;

算法与数据结构的优化;

业务过程优化;

通讯性能提升;

数据库优化;

1.2      常用优化性能的方法

1.2.1 避免不必要的对象创建

如果对象并不会随每次循环而改变状态,那么在循环中反复创建对象将带来性能损耗。高效的做法是将对象提到循环外面创建。

1.2.2 避免不必要的抛出异常

抛出异常和捕获异常属于消耗比较大的操作,在可能的情况下,应通过完善程序逻辑避免抛出不必要不必要的异常。

如:预先做参数值的判断。

如果是为了包装异常的目的(即加入更多信息后包装成新异常),那么是合理的。但是有不少代码,捕获异常没有做任何处理就再次抛出,这将无谓地增加一次捕获异常和抛出异常的消耗。如以下代码则无谓增加一次捕获与抛出异常。

Try

{

...

}

Catch(Exception ex)

{

Throw ex;

}

1.2.3 避免装箱拆箱

C#可以在值类型和引用类型之间自动转换,方法是装箱和拆箱。装箱需要从堆上分配对象并拷贝值,有一定性能消耗。如果这一过程发生在循环中或是作为底层方法被频繁调用,应当避免。

ArrayList al = new ArrayList();

for ( int i = 0 ; i < 1000 ; i ++ )

{

al.Add(i); // Implicitly boxed because Add() takes an object

}

int f = ( int )al[ 0 ];

则ArrayList可定义为List<int>

1.2.4 使用 StringBuilder 做字符串连接

如果字符串连接次数不是固定的,例如在一个循环中,则应该使用 StringBuilder 类来做字符串连接工作。因为 StringBuilder 内部有一个 StringBuffer ,连接操作不会每次分配新的字符串空间。只有当连接后的字符串超出 Buffer 大小时,才会申请新的 Buffer 空间。典型代码如下:

StringBuilder sb = new StringBuilder( 256 );

for ( int i = 0 ; i < Results.Count; i ++ )

{

sb.Append (Results[i]);

}

如果连接次数是固定的并且只有几次,此时应该直接用 + 号连接,保持程序简洁易读。实际上,编译器已经做了优化,会依据加号次数调用不同参数个数的 String.Concat 方法。例如: String str = str1 + str2 + str3 + str4; 会被编译为 String.Concat(str1, str2, str3, str4)。该方法内部会计算总的 String 长度,仅分配一次,并不会如通常想象的那样分配三次。作为一个经验值,当字符串连接操作达到 10 次以上时,则应该使用 StringBuilder。

1.2.5 避免不必要的调用 ToUpper 或 ToLower 方法 

String是不变类,调用ToUpper或ToLower方法都会导致创建一个新的字符串。如果被频繁调用,将导致频繁创建字符串对象。这违背了前面讲到的“避免频繁创建对象”这一基本原则。

例如,bool.Parse方法本身已经是忽略大小写的,调用时不要调用ToLower方法。

另一个非常普遍的场景是字符串比较。高效的做法是使用 Compare 方法,这个方法可以做大小写忽略的比较,并且不会创建新字符串。

还有一种情况是使用 HashTable 的时候,有时候无法保证传递 key 的大小写是否符合预期,往往会把 key 强制转换到大写或小写方法。实际上 HashTable 有不同的构造形式,完全支持采用忽略大小写的 key: new HashTable(StringComparer.OrdinalIgnoreCase)。

1.2.6 数据缓存

如果在我们的系统中,有一些基础数据会经常用到,如果频繁去访问数据库或文件系统读取,会由于频繁数据访问、读取、装箱成数据对象而消耗时间。我们可以预先将数据读取出这些基础数据到缓存后使用。

1.2.7 使用字典

在涉及数据列表中有大量的数据需根据其一个标识进行访问时,可以使用字典方式操作,这样相比列表操作,速度会有显著提升.

1.2.8 对象的引用代替复制

在传递数据值时,如果使用复制则会先创建一个新的实例,而引用传递的实际是对象指针,可以通过引用访问数据。

注意:被引用后有可能会被修改,这种修改是否违背开发者的本意。

1.2.9 开启虚拟加载

在使用WPF的一些控件如:TreeView,ListBox, ListView, TreeView加载大量数据时,会消耗较长的时间,可以使用虚拟化打开来提高加载速度。

具体操作例如:在XAML控件的属性中加入 VirtualizingPanel.IsVirtualizing="True" 。

1.2.10 使用静态资源

  动态资源涉及到运行时查找和对象的构建, 从而会影响到性能,静态资源是预定义的资源,可以连接到XAML属性,它类似于编译时绑定,不会影响性能。但也需要注意,静态资源需要在编译时展示。

例如:一个DataGrid控件使用一个静态资源  Style="{StaticResource dataGridStyle}"

1.2.11 资源回收

虽然.NET提供了全套的资源管理(无用资源回收:Garbage Collection,简称GC),但它不是万能的,我的系统资源是有限的,内存、文件、连接用完了要释放。

例如:在有多次数据表读取使用后,应释放对象:

Void  ReadData()

{

DataTable dt = new DataTable;

FillDataTable(ref dt);

......

// 使用完后

dtData.Dispose();

dtData = null;

GC.Collect();

}

1.2.12 重写LINQ和Lambdas表达式

如果代码需要执行很多次的时候,可能需要对LINQ或者Lambdas表达式进行重写。

下面的例子使用LINQ以及函数式风格的代码来通过编译器模型给定的名称来查找符号。

class Symbol

{

public string Name { get; private set; }

}

class Compiler

{

private List<Symbol> symbols;

public Symbol FindMatchingSymbol(string name)

{

return symbols.FirstOrDefault(s => s.Name == name);

}

}

在此过程中,这么简单的一行代码隐藏了基础内存分配开销。在上面的展开FirstOrDefault调用的例子中,代码会调用IEnumerabole<T>接口中的GetEnumerator()方法。将symbols赋值给IEnumerable<Symbol>类型的enumerable 变量,会使得对象丢失了其实际的List<T>类型信息。这就意味着当代码通过enumerable.GetEnumerator()方法获取迭代器时,.NET Framework 必须对返回的值(即迭代器,使用结构体实现)类型进行装箱从而将其赋给IEnumerable<Symbol>类型的(引用类型) enumerator变量。

解决办法是重写FindMatchingSymbol方法,将单个语句使用六行代码替代,这些代码依旧连贯,易于阅读和理解,也很容易实现。

public Symbol FindMatchingSymbol(string name)

{

foreach (Symbol s in symbols)

{

if (s.Name == name)

return s;

}

return null;

}

代码中并没有使用LINQ扩展方法,lambdas表达式和迭代器,并且没有额外的内存分配开销。

1.2.13 减少通讯的频次,组合指令

以我们XMC5400为例,上下位机指令通迅时,按照约定的通讯协议,遵循Finished与ACK机制,双方都需对接到的信号作出多次反馈,完成一次指令最少也得超过15ms,如果在一个短时间段时发送大量指令,错误率必然会增加,因此,我们采取的方式是,使用组合指令,把原先多条指令合并为一条指令,这样减少通讯的压力。组合指令按照大平台原则来组合。

1.2.14 减少通讯数据量,简化参数

有些平时不经常变的参数放到下位机,作参数简化,减少每次通讯的字节数。例如不用每次都传电流参数,速度加速度因为成对出现,可使用速度等级。

1.3      数据库性能

1.3.1 使用字段数据类型的时候,尽可能的用小的数据类型

1.3.2 针对需要的字段适当的加上索引

1.3.3 应用层面进行优化

例如加上缓存,或者页面静态化,让后面的请求不再查询数据库,这样效率更高,将效率分摊至前端方便扩展应用服务器

1.3.4 多数据库布署

在有大量的表与数据时,可以使用主从、读写分离的方式减轻数据库服务器的压力,避免在单台机器上过度消耗资源

1.3.5 垂直分表

如果一张表字段过多且有一部分字段经常读写另一部分字段不常读写,可以对数据表进行拆分,将不经常读的内容和经常操作的内容放置不同表中。

1.3.6 不读取非需内容,降低磁盘IO

1.3.7 可以使用分区表技术,将一个表分成多个不同的文件

1.3.8 尽量不要使用like

1.3.9 搜索时加上主键,不用或者少用全文索引

1.3.10 优化相关配置的参数

如最大连接数, 恢复模式。

1.3.11 尽量不使字段为NULL值

1.3.12 尽量不在 where 子句中使用 or 来连接条件

1.3.13 使用Bulk操作

在大批量数据写入与更新时,可采用BULK操作,记录越多,越体现该操作的时间性能。

1.4      无需过早优化

有些优化对在整体软件性能言而并不能在一程度上改善,例如:我们检索一个仅包含几个元素的列表,那就并不需要对此再做一个字典或映射;有些功能使用频率低,而本已经满足要求的,可不需再做优化。

C#软件性能优化的更多相关文章

  1. Java 性能优化(一)

    Java 性能调优(一) 1.衡量程序性能的标准 (1) 程序响应速度: (2) 内存占有情况: 2.程序调优措施 (1) 设计调优 设计调优处于所有调优手段 的上层,需要在软件开发之前进行.在软件开 ...

  2. [windows操作系统]内核性能剖析

    profile这个词有(1)外形.轮廓.外观.形象(2)印象.形象(3)人物简介(4)剖面图.侧面图等意.在计算机和通讯协议中这个词也非常常见.这里主要介绍一下它在软件系统性能分析领域的一个释义. 翻 ...

  3. 如何打造千万级Feed流系统

    from:https://www.cnblogs.com/taozi32/p/9711413.html 在互联网领域,尤其现在的移动互联网时代,Feed流产品是非常常见的,比如我们每天都会用到的朋友圈 ...

  4. 2014年10月Android面试总结

    最近打算跳槽,所以到外面逛了一圈,发现外面的世界还是比较精彩的,同时也认识了自己的一些不足,以及作为一个Android开发者,自己后面需要掌握的东西做一下列举. 先介绍下本人的工作经历吧,本人11年7 ...

  5. [转载]PhotoShop性能优化

    现在随着Photoshop版本越来越高功能也越来越强大,而往往强大的功能需要电脑有好的配置运行,比如HDR.图像合成或者3D和视频等类似的功能,还有处理比较大尺寸的图像时,如果电脑配置不够强往往非常卡 ...

  6. 项目设计&重构&性能优化

    漫谈项目设计&重构&性能优化 重构的好处:重构能够改进软件设计,随着项目需求的变更,项目体积的变大早已与最初的设计大相径庭,代码结构变得凌乱.复杂,如果不进行重构,则很难添加新的功能. ...

  7. 漫谈项目设计&重构&性能优化

    重构的好处:重构能够改进软件设计,随着项目需求的变更,项目体积的变大早已与最初的设计大相径庭,代码结构变得凌乱.复杂,如果不进行重构,则很难添加新的功能. 1.使项目代码更容易理解很多情况下是由于项目 ...

  8. 《嵌入式Linux内存使用与性能优化》笔记

    这本书有两个关切点:系统内存(用户层)和性能优化. 这本书和Brendan Gregg的<Systems Performance>相比,无论是技术层次还是更高的理论都有较大差距.但是这不影 ...

  9. 如何大幅优化solr的查询性能(转)

    提升软件性能,通常喜欢去调整各种启动参数,这没有多大意义,小伎俩. 性能优化要从架构和策略入手,才有可能得到较大的收益 Solr的查询是基于Field的,以Field为基本单元,例如一个文章站要索引 ...

随机推荐

  1. ceph在centos7下一个不容易发现的改变

    在centos6以及以前的osd版本,在启动osd的时候,回去根据ceph.conf的配置文件进行挂载osd,然后进行进程的启动,这个格式是这样的 [osd.0] host = hostname de ...

  2. Android10_原理机制系列_AMS之AMS的启动

    概述 该篇基于AndroidQ,主要介绍系统启动中的 AMS(ActivityManagerService)的启动过程. AMS对四大组件(AndroidQ将activity移到了ActivityTa ...

  3. 12种不宜使用的Javascript语法 ---阮一峰

    原文链接-阮一峰博客 1. == Javascript有两组相等运算符,一组是==和!=,另一组是===和!==.前者只比较值的相等,后者除了值以外,还比较类型是否相同. 请尽量不要使用前一组,永远只 ...

  4. JWT(JSON Web Token)入门

    简介 JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案 一.跨域认证的问题 互联网服务离不开用户认证.一般流程是下面这样. 1.用户向服务器发送用户名和密码. 2.服务器验证 ...

  5. java后端开发三年!你还不了解Spring 依赖注入,凭什么给你涨薪

    前言 前两天和一个同学吃饭的时候同学跟我说了一件事,说他公司有个做了两年的人向他提出要涨薪资,他就顺口问了一个问题关于spring依赖注入的,那个要求涨薪的同学居然被问懵了...事后回家想了想这一块确 ...

  6. 主板上来了一个新邻居,CPU慌了!

    大家好,我是CPU一号车间的那个阿Q,好久不见,我想死你们了- 不认识我的请去这里这里补补课:完了!CPU一味求快出事儿了! 主板上的新邻居 "阿Q,快别忙了,马上去一趟会议室,领导有重要事 ...

  7. 轻松将CAD文件转为加密的PDF文件

    对于从事设计相关工作的朋友来说,CAD肯定再熟悉不过了.一些有特殊要求的CAD文件,需要将其转换成为PDF文件以方便保存.传输.打印,同时还得保证设计图稿的安全性,所以将CAD文件直接转为加密的PDF ...

  8. 详讲FL Studio通道设置菜单

    我们在FL Studio"通道设置按钮"上右击鼠标就会弹出一个设置菜单,它包含了通道操作的各种常用命令.下文小编将会为大家详细讲解这些命令的具体作用,一起来学习吧! 1.首先,我们 ...

  9. EasyRecovery扫描预览功能,助你选择需要的数据恢复

    说到数据恢复,很多人都会选择EasyRecovery,EasyRecovery作为一个功能性还不错的数据恢复软件,能够帮你恢复丢失的数据以及重建文件系统. 在数据恢复的同时,EasyRecovery还 ...

  10. 【PYTEST】第四章Fixture

    知识点: 利用fixture共享数据 conftest.py共享fixture 使用多个fixture fixture作用范围 usefixture 重命名 1. 利用fixture共享数据 test ...