不一樣Framework下StringBuilder和String的性能對比,及不一樣Framework性能比(附Demo)

本文版權歸mephisto和博客園共有,歡迎轉載,但須保留此段聲明,並給出原文連接,謝謝合做。git

文章是哥(mephisto)寫的,SourceLinkgithub

閱讀目錄

本文版權歸mephisto和博客園共有,歡迎轉載,但須保留此段聲明,並給出原文連接,謝謝合做。web

文章是哥(mephisto)寫的,SourceLink面試

 

介紹

    本身對String和StringBuilder的處理機制略懂,大膽的設想下二者的性能對比會出現什麼樣的使人意外的狀況。因而便有此文,結果我也不知道,那麼咱們就根據設計的測試用例來看看現象吧。本文耗時5個小時(包括測試用例),但願你們給點汗水的推薦。算法

環境搭建

一:搭建多Framework測試解決方案

  因爲要測試多個Framework下的性能,因此就建立了個解決方案,版本包括:2.0,3.0,3.5,4.0,4.5。這麼多的工程,測試起來也比較麻煩,俺仍是耐心的進行了測試。app

  IDE採用的VS2013,因此你們要下載後本身運行Demo,仍是要注意下IDE版本,如是低版本,本身將4.5的移除便可dom

二:主要類及方法介紹

1:TestUtil類分佈式

  用於String和StringBuilder的建立的工具類ide

  1.1:CreateString()工具

  建立String方法

 1         public string CreateString(int num, string value)
 2  { 3 string temp = string.Empty; 4 while (num > 0) 5  { 6 temp += value; 7 num--; 8  } 9 10 return temp; 11 }

  1.2:CreateStringBuilder()

  建立StringBuilder方法

 1         public string CreateStringBuilder(int num, string value)
 2  { 3 StringBuilder sb = new StringBuilder(); 4 while (num > 0) 5  { 6  sb.Append(value); 7 num--; 8  } 9 10 return sb.ToString(); 11 }

  2:Test類

  2.1:RunStringMinInF2()

  運行String最小次數測試

 1         private static readonly int runCount = 10000000;
 2         public static void RunStringMinInF2(TestUtil testUtil, string word) 3  { 4 DateTime date1 = DateTime.Now; 5 6 int i = runCount * 5; 7 while (i > 0) 8  { 9 testUtil.CreateString(2, word); 10 i--; 11  } 12 DateTime date2 = DateTime.Now; 13 Console.WriteLine("stringMin:" + (date2 - date1).TotalMilliseconds.ToString()); 14 }

  2.2:RunSBMinInF2()

  運行StringBuilder最小次數測試

 1         public static void RunSBMinInF2(TestUtil testUtil, string word)
 2         {
 3             DateTime date1 = DateTime.Now;
 4 
 5             int i = runCount * 5;
 6             while (i > 0)
 7             {
 8                 testUtil.CreateStringBuilder(2, word);
 9                 i--;
10             }
11             DateTime date2 = DateTime.Now;
12             Console.WriteLine("stringbuilderMin:" + (date2 - date1).TotalMilliseconds.ToString());
13         }

  2.3:RunString10InF2()

  運行單個String實體調用10次相加測試

 1         public static void RunString10InF2(TestUtil testUtil, string word)
 2         {
 3             DateTime date1 = DateTime.Now;
 4 
 5             int i = runCount;
 6             while (i > 0)
 7             {
 8                 testUtil.CreateString(10, word);
 9                 i--;
10             }
11             DateTime date2 = DateTime.Now;
12             Console.WriteLine("string10:" + (date2 - date1).TotalMilliseconds.ToString());
13         }

  2.4:RunSB10InF2()

  運行單個StringBuilder實體調用10次相加測試

 1         public static void RunSB10InF2(TestUtil testUtil, string word)
 2         {
 3             DateTime date1 = DateTime.Now;
 4 
 5             int i = runCount;
 6             while (i > 0)
 7             {
 8                 testUtil.CreateStringBuilder(10, word);
 9                 i--;
10             }
11             DateTime date2 = DateTime.Now;
12             Console.WriteLine("stringbuilder10:" + (date2 - date1).TotalMilliseconds.ToString());
13         }

測試用例

一:在Framework2.0下最少字段數相加

  測試字符串:「a」

  單個實例測試數據次數:2

  被調用總次數:10000000

  測試代碼以下:

 1     class Program
 2  { 3 static void Main(string[] args) 4  { 5 TestUtil testUtil = new TestUtil(); 6 7 Test.RunStringMinInF2(testUtil, "a"); 8 Test.RunSBMinInF2(testUtil, "a"); 9 Test.RunSBMinInF2(testUtil, "a"); 10 Test.RunStringMinInF2(testUtil, "a"); 11 12  Console.Read(); 13  } 14 }

  防止因爲前後調用緣由,因此測試代碼中同一個方法出現2次。屢次運行,基本上結果差很少,同一次運行中也有數據相差的,但差異很小(幾十ms)。

  運行截圖以下:

  

  結果中在這樣極端的考驗StringBuilder性能的條件下,StringBuilder果真敗下陣來。

 二:在Framework2.0下最少字段數相加(字符串增長)

  測試字符串:「abcdefghijklmn1234567890opqrst~!@#^&*()」

  其餘測試步驟:同測試方案一

  測試代碼以下:

 1         static void Main(string[] args)
 2  { 3 TestUtil testUtil = new TestUtil(); 4 5 Test.RunStringMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 6 Test.RunSBMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 7 Test.RunSBMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 8 Test.RunStringMinInF2(testUtil, "abcdefghijklmn1234567890opqrst~!@#^&*()"); 9 10  Console.Read(); 11 }

  運行截圖以下:

  

  很顯然字符串增長了,對二者的處理時間都增長了,StringBuilder仍是必然的敗在陣來。

  三:在多Framework下最少字段數相加

  其餘測試步驟:同測試方案一

  測試結果以下:

  

  發如今4.0和4.5的版本中StringBuilder是比原來的版本有必定優點的,在StringBuilder上4.0和4.5優點都很大,在String上4.5較其餘版本略微有點優點。看來微軟也是在進步啊,你們是否是很期待開源了的Framework,不少牛人能夠對其進行貢獻,那性能不是又進步了。

  四:在多Framework下最少字段數相加(字符串增長)

  其餘測試步驟:同測試方案二

  測試結果以下:

  

  幾個Framework版本結果差很少,可是在StringBuilder上4.0和4.5敗在陣來,這是爲何列,你們思考一下,稍會做答

  五:在Framework2.0下10字段數相加

  測試字符串:「a」

  單個實例測試數據次數:10

  被調用總次數:10000000

  測試代碼以下:

 1     class Program
 2  { 3 static void Main(string[] args) 4  { 5 TestUtil testUtil = new TestUtil(); 6 7 Test.RunString10InF2(testUtil, "a"); 8 Test.RunSB10InF2(testUtil, "a"); 9 Test.RunSB10InF2(testUtil, "a"); 10 Test.RunString10InF2(testUtil, "a"); 11 12  Console.Read(); 13  } 14 }

  測試結果以下:

  對單個實例調用次數增長,StringBuilder的優點就來了,漸漸的String就只能當吊車尾了。

  六:在Framework2.0下10字段數相加(字符串增長)

  測試字符串:「abcdefghijklmn1234567890opqrst~!@#^&*()」

  其餘測試步驟:同測試方案五

  測試結果以下:

  這個測試很出乎意料啊,跟測試方案二,string的性能差異這麼多,StringBuilder沒什麼差異,你們也能夠思考下。

  

  七:在多Framework下10字段數相加

  其餘測試步驟:同測試方案五

  測試結果以下:

  

  在衆多Framework中,4.5和4.0拖妥妥的排在前2位。

  

  八:在多Framework下10字段數相加(字符串增長)

  其餘測試步驟:同測試方案六

  測試結果以下:

  

  運行性能都差很少,區別不大。

MSDN說明

一:String和 StringBuilder的類型

  雖然 StringBuilder 和 String 兩個表示字符序列,但它們以不一樣的方式實現。  String   是不可變的類型。  即看似修改 String 對象的每一個操做實際建立新的字符串。

  對執行普遍的字符串操做實例 (如修改循環中的字符串) ,修改字符串能重複精確嚴重的性能損失。  方法是使用 StringBuilder,它是易失的字符串選件類。  可變性意味着,一旦選件類的實例建立的,則能夠將其追加,取消,替換或插入字符修改。  StringBuilder   對象維護緩衝區容納擴展到該字符串。  若是有足夠的空間,新數據將被追加到緩衝區;不然,將分配一個新的、更大的緩衝區,原始緩衝區中的數據被複制到新的緩衝區,而後將新數據追加到新的緩衝區。

  雖然 StringBuilder 選件類比 String 選件類一般提供更好的性能,當不該使用 StringBuilder 自動替換 String 時,就要操做字符串。  性能取決於該字符串的大小,對新的字符串將分配的內存量,您的系統 app 的執行和操做的類型。  您應準備測試您的應用程序肯定 StringBuilder 其實是否可顯着提升性能。

  

在這些條件下考慮使用 String 選件類:

  • 當您的應用程序將對字符串更改的數量很小。  在這些狀況下,StringBuilder 不可能提供在 String的忽略或性能改進。 

  • 當執行串聯運算的內置的數字,尤爲是對於字符串文本。  在這種狀況下,編譯器可能將串聯運算到單個操做。 

  • 當您生成字符串時,當您必須執行普遍的搜索操做。  StringBuilder   選件類沒有搜索方法 ,如 IndexOf 或 StartsWith。  您必須轉換爲 String 的 StringBuilder 對象這些操做的,這樣,能夠對從使用 StringBuilder的性能。  有關詳細信息,請參閱 搜索在 StringBuilder 對象的文本 部分。 

在這些條件下考慮使用 StringBuilder 選件類:

  • 當您但願您的應用程序建立一個未知的設置爲字符串的更改在設計時 (例如,當您使用循環鏈接包含用戶輸入的隨機數字符串)。

  • 當您但願您的應用程序建立一個大量爲字符串的更改。

  StringBuilder    對象的默認值容量爲 16 個字符。

個人理解

  雖然String是引用類型,可是MS處理的時候,好比「+=」的運算符處理String的時候,是從新申請一塊託管堆,用來存儲處理後的新String,這樣新String的HashCode(地址)也會隨着變化,畢竟是一個新的地址引用,因此表面上很像是個「值類型」。

  所以,MS爲了處理Sring帶來的潛在性能問題,就加入了StringBuilder,固然咱們這裏自只用到了StringBuilder不多一部分功能(字符串相加),StringBuilder還有不少其餘性能很好的功能(這裏不表)。我的StringBuilder確定比String複雜,初始化應該是要慢一點,確實咱們在上面測試中的用例1,3告訴咱們,二者實例化差距仍是很大的。在處理多個字符串相加的時候,性能上仍是StringBuilder要強些個人理解是:StringBulider的對面默認值容量是16個字符(Framework4.5),因此當處理的字符串大於16的時候,就會從新申請開闢另外一塊當前StringBuilder字符長度,(每當追加操做致使 StringBuilder 對象的長度超過其容量,其現有的容量翻倍),這是佔必定的性能損耗的,而String每次都是申請一塊託管堆,因此StringBuilder在多字符串處理的時候性能應該是大於等於String(不考慮內部算法邏輯,只是猜想)

  在不一樣的Framework中,StringBuilder的性能有時候很快,有時候很慢,這是能夠理解的,畢竟設計一個類是有使用場景的,而已StringBuilder也提供了修改默認容量的方法,只是咱們在使用的時候沒有根據現實場景對這個進行設置。因此極小的字符串相加處理,就能夠直接用String來用,可是若是很是頻繁的處理,好比webApi被調用,那麼是否是考慮用StringBuilder來,而且,經過測試來預估StringBuilder的默認容量範圍,這樣來達到性能的極致。

  在不一樣的Framework中,2.0比1.0是有個革命性的變化,好比泛型等等。3.0和3.5加了不少功能,好比WPF,WCF,WFF,Linq等,但他們只是在2.0的基礎上作了擴展,豐富,實際底層改變不大,你們從3.0和3.5的安裝版就能夠看出,他們都是100多M,都是經過打補丁的方式來弄的,因此安裝包很大。到了4.0,Framework發生的大變化,我的以爲最實質的變化就是引入了System.Core。而且把全部的都從新實現了一遍,因此安裝版只有40多M,加了這麼多東西,從28->40非常能夠的。4.5沒有研究,但從4.5引用的仍是4.0的System.Core,就應該是底層變化不大,若是你們有知道,瞭解的,但願告訴下咱們。因此上面結果中,有時候4.0,4.5領先不少,在有的時候落後不少,也沒什麼的,估計只是策略變了而已。

  Framework也開源了,你們均可以去關注下,我也會抽時間去研究研究,看何時能在未來的Framework中看到本身提交的代碼,那是多麼愉悅的事情啊,多謝了github這個分佈式版本管理,咱們就能夠修改提交了,審覈過不過無所謂,畢竟提交過,哈哈。若是有一天能審覈過,那能夠面試的時候或者跟猿們聊天的時候,多BB啊。

  一寫就到了23:30了,明天還要上班,收筆把,還準備把反射出的不一樣版本的String和StringBuilder的代碼貼出來的,打住了,身體也很重要。

Demo下載

源碼下載

本文版權歸mephisto和博客園共有,歡迎轉載,但須保留此段聲明,並給出原文連接,謝謝合做。

文章是哥(mephisto)寫的,SourceLink

相關文章
相關標籤/搜索