我最近在研讀《CLR via C#》,其中有一個章節講String類型,Jeffrey說當進行不區分大小寫的字符串對比時,應當儘可能多的使用ToUpperInvariant,由於一方面CLR對轉換爲大寫的操做進行了專門的優化,使其性能更加卓越;另外一方面ToUpperInvariant和ToLowerInvariant忽略了對語言文化的處理,所以性能比ToUpper和ToLower要快得多。數組
從理論層面上來說,這應該是正確的。可是究竟能快多少呢?或者說這種性能提高值不值得咱們付出專門的精力來關注呢?函數
咱們下面就進行一個測試,看看它們究竟能對性能產生多大影響。性能
首先,我實例化了一個 char 類型數組,並使用 Char Code 在 0 到 133 之間的隨機字符填滿,咱們就獲得了一個隨機的測試用例,其中包含半角符號、小寫英文字母和大寫英文字母。測試
而後我要分別測試 Char 類型的四個靜態函數和 String 類型的四個實例函數,我猜想 String 類型中的實例函數實際上也是經過 Char 類型中相對應的靜態函數實現的。優化
爲了不CLR的內存優化對測試結果產生影響,每執行一項測試以前,都將在堆中建立新的示例,並從測試用例中複製成員。spa
除此以外,爲了儘可能保證測試結果的正確性,我將循環執行並記錄測試代碼 N 次,以後取它們的去權平均值。code
=====================我是分割線=====================對象
下面是測試代碼:blog
填充數組:內存
1 static void Fill() 2 { 3 //實例化一個容納10000個字符的數組 4 letters = new char[maxElementsCount]; 5 //填充數組 6 for (int i = 0; i < maxElementsCount; i++) 7 { 8 var charcode = R.Next(0, 133); 9 letters[i] = Convert.ToChar(charcode); 10 } 11 }
執行 Char 靜態方法測試的執行函數:
1 static double Invoke(Action<char[]> func,string testname) 2 { 3 char[] source = new char[maxElementsCount]; 4 letters.CopyTo(source, 0); 5 DateTime start = DateTime.Now; //當前時間 6 DateTime end = DateTime.Now; 7 Console.WriteLine("開始測試 "+ testname + " [開始時間:" + start.ToString() + "]"); 8 func(source); 9 end = DateTime.Now; 10 var during = (end - start).TotalMilliseconds; 11 Console.WriteLine("測試 " + testname + " 完成,耗時" + during + "毫秒[結束時間:" + end.ToString() + "]"); 12 return during; 13 }
執行 String 實例方法的執行函數:
1 static double InvokeString(Action<string> func, string testname) 2 { 3 string source = new string(letters); 4 DateTime start = DateTime.Now; //當前時間 5 DateTime end = DateTime.Now; 6 Console.WriteLine("開始測試 " + testname + " [開始時間:" + start.ToString() + "]"); 7 func(source); 8 end = DateTime.Now; 9 var during = (end - start).TotalMilliseconds; 10 Console.WriteLine("測試 " + testname + " 完成,耗時" + during + "毫秒[結束時間:" + end.ToString() + "]"); 11 return during; 12 }
測試方法:
1 static void Run(int maxElementsCount) 2 { 3 Fill(); 4 5 char_tolower.Add(Invoke(s => 6 { 7 for (int i = 0; i < s.Length; i++) 8 { 9 char.ToLower(s[i]); 10 } 11 }, "Char.ToLower")); 12 13 char_toupper.Add(Invoke(s => 14 { 15 for (int i = 0; i < s.Length; i++) 16 { 17 char.ToUpper(s[i]); 18 } 19 }, "Char.ToUpper")); 20 21 char_tolowerinvariant.Add(Invoke(s => 22 { 23 for (int i = 0; i < s.Length; i++) 24 { 25 char.ToLowerInvariant(s[i]); 26 } 27 }, "Char.ToLowerInvariant")); 28 29 char_toupperinvariant.Add(Invoke(s => 30 { 31 for (int i = 0; i < s.Length; i++) 32 { 33 char.ToUpperInvariant(s[i]); 34 } 35 }, "Char.ToUpperInvariant")); 36 37 string_tolower.Add(InvokeString(s => 38 { 39 s.ToLower(); 40 }, "String.ToLower")); 41 42 string_toupper.Add(InvokeString(s => 43 { 44 s.ToUpper(); 45 }, "String.ToUpper")); 46 47 string_tolowerinvariant.Add(InvokeString(s => 48 { 49 s.ToLowerInvariant(); 50 }, "String.ToLowerInvariant")); 51 52 string_toupperinvariant.Add(InvokeString(s => 53 { 54 s.ToUpperInvariant(); 55 }, "String.ToUpperInvariant")); 56 }
如下是循環執行1000次的去權平均數:
從結果中咱們能夠看出,ToLowerInvariant 和 ToUpperInvariant 由於忽略的語言信息,性能的確是比關注語言信息的 ToLower 和 ToUpper 要快一些。可是Char.ToUpper的性能比Char.ToLower的性能居然還要差,這多是因爲隨機的測試數據不夠隨機形成的。
此外,還能夠得出 String 類型的四個實例函數並不是經過 Char 中的靜態函數實現的 , 這應該是由於.NET CLR 中定義的 String 是一個單獨的基元類型,而並不是是 Char 數組 , 所以通過了單獨的實現,而且性能明顯要好過 Char 的數組。
可是我這裏用的測試數據是一個長達 100萬 字的字符串,在這種狀況下的性能損失不足0.1毫秒,實在是不值一提。我測試用的電腦是 i5 4代處理器,相信在計算能力更強的處理器上,這個數值將被無限的縮小。所以咱們在實際開發中幾乎不須要特別關注應當用哪一個方法,特別是若是在作國際化的應用時,ToLowerInvariant 和 ToUpperInvariant 雖然可以帶來性能的提高,可是也可能致使隱形BUG,並不值得推廣。此外,在開發中咱們應當儘可能少的使用 Char 類型,一個 Char 類型對象要單獨佔用一個棧內存地址,且只能存儲一個字符,實在是太不合算。