0

0

C#性能优化

伊谢尔伦

伊谢尔伦

发布时间:2016-12-05 10:16:42

|

1429人浏览过

|

来源于php中文网

原创

性能是考量一个软件产品好坏的重要指标,与产品的功能有着同等重要的地位。用户在选择一款软件产品的时候基本都会亲身试验比较同类产品的性能。作为选购那个软件重要因素之一。

软件的性能指什么
1.降级内存消耗 
在软件开发中,内存消耗一般作为次要的考虑,因为现在的计算机一般都拥有比较大的内存,很多情况下,性能优化的手段就是空间换取时间。但是,并不是说,我们可以肆无忌惮的挥霍内存。如果需要支持在大数据量的用例时,如果内存被耗尽,操作系统会发生频繁的内外存交换。导致执行速度急剧下降
2.提升执行速度

加载速度。

特定操作的响应速度。包括,点击,键盘输入,滚动,排序过滤等。


性能优化的原则

理解需求 
以MultiRow产品为例,MultiRow的一个性能需求是:"百万行数据绑定下平滑滚动。"整个MultiRow项目的开发过程一直要考虑这个目标。

理解瓶颈 
根据经验,99%的性能消耗是由于1%的代码造成的。所以,大部分性能优化都是针对这1%的瓶颈代码进行的。具体实施也就分为两步。首先,确定瓶颈,其次消除瓶颈。

切忌过度 
首先必须要认识到,性能优化本身是有成本的。这个成本不单单体现在做性能优化所付出的工作量。还包括为性能优化而写出的复杂代码,额外的维护成本,会引入新的Bug,额外的内存开销等。 一个常见问题是,一些刚接触软件开发的同学会对一些不必要的点生搬硬套性能优化技巧或者设计模式,带来不必要的复杂度。性能优化常常需要对收益和成本之间做出权衡。


如何发现性能瓶颈

上一节提到,性能优化的第一步就是发现性能瓶颈,这一节主要介绍定位性能瓶颈的一些实践。

1. 如何获取内存消耗

以下代码可以获取某个操作的内存消耗。

// 在这里写一些可能消耗内存的代码,例如,如果想了解创建一个GcMultiRow软件需要多少内存可以执行以下代码
long start = GC.GetTotalMemory(true);
var gcMulitRow1 = new GcMultiRow();
GC.Collect();
// 确保所有内存都被GC回收
GC.WaitForFullGCComplete();
long end = GC.GetTotalMemory(true);
long useMemory = end - start;

2. 如何获取时间消耗 
以下代码可以获取某个操作时间消耗。

System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
for (int i = 0; i < 1000; i++)
{
     gcMultiRow1.Sort();
}
watch.Stop();
var useTime = (double)watch.ElapsedMilliseconds / 1000;

这里把一个操作循环执行了1000次,最后再把消耗的时间除以1000来确定最终消耗的时间。可以是结果更准确稳定,排除意外数据。

3. 通过CodeReview发现性能问题。 
很多情况下,可以通过CodeReview发现性能问题。对于大数据量的循环,要格外关注。循环内的逻辑应该执行的尽可能的快。4.ANTS Performance Profiler 
ANTS Profiler是款功能强大的性能检测软件。可以很好的帮助我们发现性能瓶颈。使用这款软件定位性能瓶颈可以起到事半功倍的效果。熟练使用这个工具,我们可以快速准确的定位到有性能问题的代码。 这个工具很强大,但是也并不是完美无缺的。首先,这是一款收费软件,部门只有几个许可号。其次,这个软件的工作原理是在IL中加入一些钩子,用来记录时间。所以在分析时,软件的执行速度会比实际运行慢一些获得的数据也因此并不是百分之百的准确,应该把软件分析的数据作为参考,帮助快速定位问题,但是不要完全依赖,还要结合其他技巧来分析程序的性能。

性能优化的方法和技巧

定位了性能问题后,解决的办法有很多。这个章节会介绍一些性能优化的技巧和实践。

1. 优化程序结构

对于程序结构,在设计时就应该考虑,评估是否可以达到性能需求。如果后期发现了性能问题需要考虑调整结构会带来非常大的开销。举例:

1.1 GcMultiRowGcMultiRow要支持100万行数据,假设每行有10列的话,就需要有1000万个单元格,每个单元格上又有很多的属性。如果不做任何优化的话,大数据量时,一个GcMultiRow软件的内存开销会相当的大。GcMultiRow采用的方案是使用哈希表来存储行数据。只有用户改过的行放到哈希表里,而对于大部分没有改过的行都直接使用模板代替。就达到了节省内存的目的。

1.2 Spread for WPF/Silverlight (SSL)WPF的画法和Winform不同,是通过组合View元素的方法实现的。SSL同样支持百万级的数据量,但是又不能给每个单元格都分配一个View。所以SSL使用了VirtualizePanel来实现画法。思路是每一个View是一个Cell的展示模块。可以和Cell的数据模块分离。这样。只需要为显示出来的Cell创建View。当发生滚动时会有一部分Cell滚出屏幕,有一部分Cell滚入屏幕。这时,让滚出屏幕的Cell和View分离。然后再复用这部分View给新进入屏幕的Cell。如此循环。这样只需要几百个View就可以支持很多的Cell。

XAnswer
XAnswer

XAnswer是一款可以生成思维导图的AI搜索工具,聚合全网优质信息源,结合LLM能力和RAG技术, 为用户提供实时性的搜索结果、个性化的答案呈现。

下载

2. 缓存

缓存(Cache)是性能优化中最常用的优化手段.适用的情况是频繁的获取一些数据,而每次获取这些数据需要的时间比较长。这时,第一次获取的时候会用正常的方法,并且在获取之后把数据缓存下来。之后就使用缓存的数据。 如果使用了缓存的优化方法,需要特别注意缓存数据的同步,就是说,如果真实的数据发生了变化,应该及时的清除缓存数据,确保不会因为缓存而使用了错误的数据。 举例:

2.1 使用缓存的情况比较多。最简单的情况就是缓存到一个Field或临时变量里。      

for(int i = 0; i < gcMultiRow.RowCount; i++) 
{ 
    // Do something; 
}

以上代码一般情况下是没有问题的,但是,如果GcMultiRow的行数比较大。而RowCount属性的取值又比较慢的时候就需要使用缓存来做性能优化。 

int rowCount = gcMultiRow.RowCount;
for (int i = 0; i < rowCount; i++)
{
   // Do something;
}

2.2 使用对象池也是一个常见的缓存方案,比使用Field或临时变量稍微复杂一点。 例如,在MultiRow中,画边线,画背景,需要用到大量的Brush和Pen。这些GDI对象每次用之前要创建,用完后要销毁。创建和销毁的过程是比较慢的。GcMultiRow使用的方案是创建一个GDIPool。本质上是一些Dictionary,使用颜色做Key。所以只有第一次取的时候需要创建,以后就直接使用以前创建好的。以下是GDIPool的代码: 

public static class GDIPool 
{ 
    Dictionary _cacheBrush = new Dictionary(); 
    Dictionary _cachePen = new Dictionary(); 
    public static Pen GetPen(Color color) 
   { 
       Pen pen; 
       if_cachePen.TryGetValue(color, out pen)) 
       { 
           return pen; 
       } 
       pen = new Pen(color); 
      _cachePen.Add(color, pen); 
       return pen; 
   } 
}

2.3 懒构造 
有时候,有的对象创建需要花费较长时间。而这个对象可能并不是所有的场景下都需要使用。这时,使用赖构造的方法可以有效提高性能。 举例:对象A需要内部创建对象B。对象B的构造时间比较长。 一般做法:

public class A
{
   public B _b = new B();
}

一般做法下由于构造对象A的同时要构造对象B导致了A的构造速度也变慢了。优化做法:

public class A
{
   private B _b;
   public B BProperty
   {
       get
      {
         if(_b == null)
         {
             _b = new B();
         }
         return _b;
      }
   }
}

优化后,构造A的时候就不需要创建B对象,只有需要使用的时候才需要构造B对象。


2.4  优化算法 优化算法可以有效的提高特定操作的性能,使用一种算法时应该了解算法的适用情况,最好情况和最坏情况。 以GcMultiRow为例,最初MultiRow的排序算法使用了经典的快速排序算法。这看起来是没有问题的,但是,对于表格软件,用户经常的操作是对有序表进行排序,如顺序和倒序之间切换。而经典的快速排序算法的最差情况就是基本有序的情况。所以经典快速排序算法不适合MultiRow。最后通过改的排序算法解决了这个问题。改进的快速排序算法使用了3个中点来代替经典快排的一个中点的算法。每次交换都是从3个中点中选择一个。这样,乱序和基本有序的情况都不是这个算法的最坏情况,从而优化了性能。


2.5 了解Framework提供的数据结构 我们现在工作的.net framework平台,有很多现成的数据数据结构。我们应该了解这些数据结构,提升我们程序的性能: 
举例:
2.5.1 string 的加运算符 VS StringBuilder: 字符串的操作是我们经常遇到的基本操作之一。 我们经常会写这样的代码 string str = str1 + str2。当操作的字符串很少的时候,这样的操作没有问题。但是如果大量操作的时候(例如文本文件的Save/Load, Asp.net的Render),这样做就会带来严重的性能问题。这时,我们就应该用StringBuilder来代替string的加操作。

2.5.2 Dictionary VS List Dictionary和List是最常用的两种集合类。选择正确的集合类可以很大的提升程序的性能。为了做出正确的选择,我们应该对Dictionary和List的各种操作的性能比较了解。2.5.3TryGetValue 对于Dictionary的取值,比较直接的方法是如下代码:

if(_dic.ContainKey("Key")
{
    return _dic\["Key"\];
}

当需要大量取值的时候,这样的取法会带来性能问题。优化方法如下:

object value;
if(_dic.TryGetValue("Key", out value))
{
    return value;
}

使用TryGetValue可以比先Contain再取值提高一倍的性能。


2.5.4 为Dictionary选择合适的Key。 Dictionary的取值性能很大情况下取决于做Key的对象的Equals和GetHashCode两个方法的性能。如果可以的话使用Int做Key性能最好。如果是一个自定义的Class做Key的话,最好保证以下两点:1. 不同对象的GetHashCode重复率低。2. GetHashCode和Equals方法立即简单,效率高。

2.5.5 List的Sort和BinarySearch性能很好,如果能满足功能需求的话推荐直接使用,而不是自己重写。 

List list = new List{3, 10, 15};
list.BinarySearch(10); // 对于存在的值,结果是1
list.BinarySearch(8); // 对于不存在的值,会使用负数表示位置,如查找8时,结果是-2, 查找0结果是-1,查找100结果是-4.

复制代码

2.6 通过异步提升响应时间
2.6.1 多线程 
有些操作确实需要花费比较长的时间,如果用户的操作在这段时间卡死会带来很差的用户体验。有时候,使用多线程技术可以解决这个问题 举例: CalculatorEngine在构造的时候要初始化所有的Function。由于Function比较多,初始化时间会比较长。这是就用到了多线程技术,在工作线程中做Function的初始化工作,就不影响主线程快速响应用户的其他操作了。代码如下:

public CalcParser()
{
   if (_functions == null)
   {
       lock (_obtainFunctionLocker)
       {
           if (_functions == null)
           {
               System.Threading.ThreadPool.QueueUserWorkItem((s) =>
               {
                   if (_functions == null)
                   {
                       lock (_obtainFunctionLocker)
                       {
                           if (_functions == null)
                           {
                               _functions = EnsureFunctions();
                           }
                       }
                   }
               });
           }
       }
   }
}

这里比较慢的操作就是EnsureFunctions函数,是在另一个线程里执行的,不会影响主线程的响应。当然,使用多线程是一个比较有难度的方案,需要充分考虑跨线程访问和死锁的问题。

2.6.2 加延迟时间 
在GcMultiRow实现AutoFilter功能的时候使用了一个类似于延迟执行的方案来提升响应速度。AutoFilter的功能是用户在输入的过程中根据用户的输入更新筛选的结果。数据量大的时候一次筛选需要较长时间,会影响用户的连续输入。使用多线可能是个好的方案,但是使用多线程会增加程序的复杂度。MultiRow的解决方案是当接收到用户的键盘输入消息的时候,并不立即出发Filter,而是等待0.3秒。如果用户在连续输入,会在这0.3秒内再次收到键盘消息,就再等0.3秒。直到连续0.3秒内没有新的键盘消息时再触发Filter。保证了快速响应用户输入的目的。

2.6.3 Application.Idle事件 
在GcMultiRow的Designer里,经常要根据当前的状态刷新ToolBar上按钮的Disable/Enable状态。一次刷新需要较长的时间。如果用户连续输入会有卡顿的感觉,影响用户体验。GcMultiRow的优化方案是挂系统的Application.Idle事件。当系统空闲的时候,系统会触发这个事件。接到这个事件表示此时用户已经完成了连续的输入,这时就可以从容的刷新按钮的状态了。

2.6.4 Invalidate, BeginInvoke. PostEvent 平台本身也提供了一些异步方案。 
例如;在Winform下,触发一块区域重画的时候,一般不适用Refresh而是Invalidate,这样会触发异步的刷新。在触发之前可以多次Invalidate。BeginInvoke,PostMessage也都可以触发异步的行为。

2.7 了解平台特性 
如WPF的DP DP相对于CLR property来说是很慢的,包括Get和Set都很慢,这和一般质感上Get比较快Set比较慢不一样。如果一个DP需要被多次读取的话建议是CLR property做Cache。

2.8 进度条,提升用户体验 
有时候,以上提到的方案都没有办法快速响应用户操作,进度条,一直转圈圈的图片,提示性文字如"你的操作可能需要较长时间请耐心等待"。都可以提升用户体验。可以作为最后方案来考虑。


目前已有很多使用C#编写的开发工具,其中值得一提的是ComponentOne Studio Enterprise,这是一款专注于企业应用的.NET全功能控件套包,支持WinForms、WPF、UWP、ASP.NET MVC等多个平台,帮助在缩减成本的同时,提前交付丰富的桌面、Web和移动企业应用。

   

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

相关标签:

c#

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

9

2026.01.27

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

107

2026.01.26

edge浏览器怎样设置主页 edge浏览器自定义设置教程
edge浏览器怎样设置主页 edge浏览器自定义设置教程

在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

13

2026.01.26

苹果官方查询网站 苹果手机正品激活查询入口
苹果官方查询网站 苹果手机正品激活查询入口

苹果官方查询网站主要通过 checkcoverage.apple.com/cn/zh/ 进行,可用于查询序列号(SN)对应的保修状态、激活日期及技术支持服务。此外,查找丢失设备请使用 iCloud.com/find,购买信息与物流可访问 Apple (中国大陆) 订单状态页面。

121

2026.01.26

npd人格什么意思 npd人格有什么特征
npd人格什么意思 npd人格有什么特征

NPD(Narcissistic Personality Disorder)即自恋型人格障碍,是一种心理健康问题,特点是极度夸大自我重要性、需要过度赞美与关注,同时极度缺乏共情能力,背后常掩藏着低自尊和不安全感,影响人际关系、工作和生活,通常在青少年时期开始显现,需由专业人士诊断。

6

2026.01.26

windows安全中心怎么关闭 windows安全中心怎么执行操作
windows安全中心怎么关闭 windows安全中心怎么执行操作

关闭Windows安全中心(Windows Defender)可通过系统设置暂时关闭,或使用组策略/注册表永久关闭。最简单的方法是:进入设置 > 隐私和安全性 > Windows安全中心 > 病毒和威胁防护 > 管理设置,将实时保护等选项关闭。

6

2026.01.26

2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】
2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】

铁路12306提供起售时间查询、起售提醒、购票预填、候补购票及误购限时免费退票五项服务,并强调官方渠道唯一性与信息安全。

112

2026.01.26

个人所得税税率表2026 个人所得税率最新税率表
个人所得税税率表2026 个人所得税率最新税率表

以工资薪金所得为例,应纳税额 = 应纳税所得额 × 税率 - 速算扣除数。应纳税所得额 = 月度收入 - 5000 元 - 专项扣除 - 专项附加扣除 - 依法确定的其他扣除。假设某员工月工资 10000 元,专项扣除 1000 元,专项附加扣除 2000 元,当月应纳税所得额为 10000 - 5000 - 1000 - 2000 = 2000 元,对应税率为 3%,速算扣除数为 0,则当月应纳税额为 2000×3% = 60 元。

33

2026.01.26

oppo云服务官网登录入口 oppo云服务登录手机版
oppo云服务官网登录入口 oppo云服务登录手机版

oppo云服务https://cloud.oppo.com/可以在云端安全存储您的照片、视频、联系人、便签等重要数据。当您的手机数据意外丢失或者需要更换手机时,可以随时将这些存储在云端的数据快速恢复到手机中。

101

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 7.7万人学习

C 教程
C 教程

共75课时 | 4.2万人学习

C++教程
C++教程

共115课时 | 14.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号