搜尋

C#效能優化

Dec 05, 2016 am 10:16 AM
c#

性能是考慮一個軟體產品好壞的重要指標,與產品的功能有著同等重要的地位。使用者在選擇軟體產品的時候基本上都會親身試驗比較同類產品的性能。作為選購那個軟體重要因素之一。

軟體的性能指什麼
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。


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<Color, Brush > _cacheBrush = new Dictionary<Color, Brush>(); 
    Dictionary<Color, Pen> _cachePen = new Dictionary<Color, Pen>(); 
    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<int> list = new List<int>{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和移动企业应用。

   


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
c#.net適合您嗎?評估其適用性c#.net適合您嗎?評估其適用性Apr 13, 2025 am 12:03 AM

c#.netissutableforenterprise-levelapplications withemofrosoftecosystemdueToItsStrongTyping,richlibraries,androbustperraries,androbustperformance.however,itmaynotbeidealfoross-platement forment forment forment forvepentment offependment dovelopment toveloperment toveloperment whenrawspeedsportor whenrawspeedseedpolitical politionalitable,

.NET中的C#代碼:探索編程過程.NET中的C#代碼:探索編程過程Apr 12, 2025 am 12:02 AM

C#在.NET中的編程過程包括以下步驟:1)編寫C#代碼,2)編譯為中間語言(IL),3)由.NET運行時(CLR)執行。 C#在.NET中的優勢在於其現代化語法、強大的類型系統和與.NET框架的緊密集成,適用於從桌面應用到Web服務的各種開發場景。

C#.NET:探索核心概念和編程基礎知識C#.NET:探索核心概念和編程基礎知識Apr 10, 2025 am 09:32 AM

C#是一種現代、面向對象的編程語言,由微軟開發並作為.NET框架的一部分。 1.C#支持面向對象編程(OOP),包括封裝、繼承和多態。 2.C#中的異步編程通過async和await關鍵字實現,提高應用的響應性。 3.使用LINQ可以簡潔地處理數據集合。 4.常見錯誤包括空引用異常和索引超出範圍異常,調試技巧包括使用調試器和異常處理。 5.性能優化包括使用StringBuilder和避免不必要的裝箱和拆箱。

測試C#.NET應用程序:單元,集成和端到端測試測試C#.NET應用程序:單元,集成和端到端測試Apr 09, 2025 am 12:04 AM

C#.NET應用的測試策略包括單元測試、集成測試和端到端測試。 1.單元測試確保代碼的最小單元獨立工作,使用MSTest、NUnit或xUnit框架。 2.集成測試驗證多個單元組合的功能,常用模擬數據和外部服務。 3.端到端測試模擬用戶完整操作流程,通常使用Selenium進行自動化測試。

高級C#.NET教程:ACE您的下一次高級開發人員面試高級C#.NET教程:ACE您的下一次高級開發人員面試Apr 08, 2025 am 12:06 AM

C#高級開發者面試需要掌握異步編程、LINQ、.NET框架內部工作原理等核心知識。 1.異步編程通過async和await簡化操作,提升應用響應性。 2.LINQ以SQL風格操作數據,需注意性能。 3..NET框架的CLR管理內存,垃圾回收需謹慎使用。

C#.NET面試問題和答案:提高您的專業知識C#.NET面試問題和答案:提高您的專業知識Apr 07, 2025 am 12:01 AM

C#.NET面試問題和答案包括基礎知識、核心概念和高級用法。 1)基礎知識:C#是微軟開發的面向對象語言,主要用於.NET框架。 2)核心概念:委託和事件允許動態綁定方法,LINQ提供強大查詢功能。 3)高級用法:異步編程提高響應性,表達式樹用於動態代碼構建。

使用C#.NET建築微服務:建築師實用指南使用C#.NET建築微服務:建築師實用指南Apr 06, 2025 am 12:08 AM

C#.NET是構建微服務的熱門選擇,因為其生態系統強大且支持豐富。 1)使用ASP.NETCore創建RESTfulAPI,處理訂單創建和查詢。 2)利用gRPC實現微服務間的高效通信,定義和實現訂單服務。 3)通過Docker容器化微服務,簡化部署和管理。

C#.NET安全性最佳實踐:防止常見漏洞C#.NET安全性最佳實踐:防止常見漏洞Apr 05, 2025 am 12:01 AM

C#和.NET的安全最佳實踐包括輸入驗證、輸出編碼、異常處理、以及身份驗證和授權。 1)使用正則表達式或內置方法驗證輸入,防止惡意數據進入系統。 2)輸出編碼防止XSS攻擊,使用HttpUtility.HtmlEncode方法。 3)異常處理避免信息洩露,記錄錯誤但不返回詳細信息給用戶。 4)使用ASP.NETIdentity和Claims-based授權保護應用免受未授權訪問。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用