C# 代碼性能優化舉例


普通人如果說什么事情慢,指的是 5 分鍾,10 分鍾,或者 1 個小時、2 個小時。而程序員要說什么事情慢,他們指的是 2 納秒。

 

每個納秒對程序員來說都是非常寶貴的,所以,要對代碼進行優化,優化,再優化,每個納秒都不要浪費。

在 C# 程序中,完成一件任務通常都有若干種方法,但這些方法之間是存在一些差異的,特別是性能上的差異。本文嘗試着舉幾個例子來說明這種差異。

 

1. 裝箱還是不裝箱(to box or not to box)

一般來說,值類型的數據都是在棧上操作的,而引用類型的數據都是在堆上的,而當值類型需要作為引用類型操作時,都要先對值類型數據進行裝箱,使其變為引用類型。但裝箱操作是一個成本很高的操作,要盡量避免使用。如對於如下代碼:

static string GetArrayInfo(Array a)
{
    string s = $"An array with {a.Length} elements.";
    return s;
}

其中,a.Length 是一個整型的值類型變量,與字符串混合在一起時,需要進行裝箱操作。可改為如下代碼以避免裝箱操作:

static string GetArrayInfo(Array a)
{
    string s = "An array with " + a.Length.ToString() + " elements.";
    return s;
}

 

2. 構造字符串(building strings) 

構造字符串,可以使用 StringBuilder,也可以直接用 + 操作符(編譯為對 Concat() 方法的調用)連接字符串。一般來說,使用 StringBuilder 是一種高性能的字符串構造方案,但也不盡然,尤其是 StringBuilder 的 AppendFormat() 方法,成本很高,應盡量避免使用。舉例如下。

static string Greeting(string name)
{
    StringBuilder sb = new();
    _ = sb.AppendFormat("Hello, {0}", name ?? "null");
    return sb.ToString();
}

下面的代碼使用 Append() 方法構造字符串:

static string Greeting(string name)
{
    StringBuilder sb = new();
    _ = sb.Append("Hello, ");
    _ = sb.Append(name ?? "null");
    return sb.ToString();
}

最后,我們用 + 操作符(Concat() 方法)構造同樣的字符串:

static string Greeting(string name)
{
    return "Hello, " + name ?? "null";
}

經過實測,+ 操作符方法(Concat() 方法)性能最好,StringBuilder 的 Append() 次之,AppendFormat() 最差。

 

3. 類型轉換(type conversion)

如將一個數字字符串轉換為整型數,一般有三種方法實現轉換:

// 方法1:
_ = int.TryParse("123", out int n);

// 方法2:
int n = int.Parse("123");

// 方法3:
int n = Convert.ToInt32("123");

這三種方法都可以。從安全性上講,int.TryParse() 最安全,從性能上說,則 Convert.ToInt32() 最好(但也相差不大)。如果能確定輸入字符串的合法性,則盡量使用 Convert.ToInt32() 方法,反之,則使用 int.TryParse() 以避免拋出異常。

 

以上三個實例的實測數據如下圖所示,這是經過 100 次循環,每次循環執行 1000000 次調用,經過平均后得出的結果。

 

提高程序的運行性能,除了選用高性能的硬件(高性能CPU、內存、硬盤等)之外,在軟件上也要下功夫進行優化。優化涉及的方面很多,代碼優化是其中很重要的環節。本文從“黑盒”角度舉了幾個例子並進行實際測試,以揭示不同方法的實現性能。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM