[英]Why is StringBuilder slower than string concatenation?
與+串聯相比,為什么StringBuilder
變慢? StringBuilder
旨在避免額外的對象創建,但為什么它會懲罰性能呢?
static void Main(string[] args)
{
int max = 1000000;
for (int times = 0; times < 5; times++)
{
Console.WriteLine("\ntime: {0}", (times+1).ToString());
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
sw.Stop();
Console.WriteLine("String +\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
sw = Stopwatch.StartNew();
for (int j = 0; j < max; j++)
{
StringBuilder msg = new StringBuilder();
msg.Append("Your total is ");
msg.Append("$500 ");
msg.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
Console.Read();
}
編輯:按建議移出范圍變量:
更改以便不會一直實例化StringBuilder,而是.Clear()
它:
time: 1
String + : 3348ms
StringBuilder : 3151ms
time: 2
String + : 3346ms
StringBuilder : 3050ms
等等
請注意 ,這仍然測試完全相同的功能,但嘗試更智能地重用資源。
代碼:(也訪問http://ideone.com/YuaqY )
using System;
using System.Text;
using System.Diagnostics;
public class Program
{
static void Main(string[] args)
{
int max = 1000000;
for (int times = 0; times < 5; times++)
{
{
Console.WriteLine("\ntime: {0}", (times+1).ToString());
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
sw.Stop();
Console.WriteLine("String +\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
{
Stopwatch sw = Stopwatch.StartNew();
StringBuilder msg = new StringBuilder();
for (int j = 0; j < max; j++)
{
msg.Clear();
msg.Append("Your total is ");
msg.Append("$500 ");
msg.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
}
Console.Read();
}
}
您正在每次迭代時創建一個StringBuilder
的新實例,這會產生一些開銷。 既然你沒有將它用於它實際意圖做的事情(即:構建大字符串,否則需要許多字符串連接操作),看到比串聯更糟糕的性能並不奇怪。
StringBuilder
更常見的比較/用法如下:
string msg = "";
for (int i = 0; i < max; i++)
{
msg += "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
StringBuilder msg_sb = new StringBuilder();
for (int j = 0; j < max; j++)
{
msg_sb.Append("Your total is ");
msg_sb.Append("$500 ");
msg_sb.Append(DateTime.Now);
}
有了這個,你會發現StringBuilder
和串聯之間存在顯着的性能差異。 而“重要”是指數量級 ,而不是您在示例中觀察到的~10%的差異。
由於StringBuilder
不需要構建大量的中間字符串而不會被丟棄,因此可以獲得更好的性能。 這就是它的意思。 對於較小的字符串,最好使用字符串連接以簡化和清晰。
使用更長的字符串時,StringBuilder的好處應該是顯而易見的。
每次連接字符串時都會創建一個新的字符串對象,因此字符串越長,從舊字符串復制到新字符串所需的越多。
此外,創建許多臨時對象可能會對StopWatch無法測量的性能產生負面影響,因為它會使用臨時對象“污染”托管堆,並可能導致更多垃圾回收周期。
修改你的測試以創建(更多)更長的字符串並使用(更多)更多的連接/追加操作,並且StringBuilder應該表現更好。
除了不以最有效的方式使用StringBuilder
之外,您還沒有盡可能有效地使用字符串連接。 如果你知道你提前連接了多少個字符串,那么在一行上完成這一切應該是最快的。 編譯器優化操作,以便不生成任何中間字符串。
我添加了幾個測試用例。 一個與sehe建議的基本相同,另一個在一行中生成字符串:
sw = Stopwatch.StartNew();
builder = new StringBuilder();
for (int j = 0; j < max; j++)
{
builder.Clear();
builder.Append("Your total is ");
builder.Append("$500 ");
builder.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder (clearing)\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
msg = "Your total is " + "$500" + DateTime.Now;
}
sw.Stop();
Console.WriteLine("String + (one line)\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
以下是我在機器上看到的輸出示例:
time: 1
String + : 3707ms
StringBuilder : 3910ms
StringBuilder (clearing) : 3683ms
String + (one line) : 3645ms
time: 2
String + : 3703ms
StringBuilder : 3926ms
StringBuilder (clearing) : 3666ms
String + (one line) : 3625ms
一般來說: - 如果你在很多步驟中構建一個大字符串,或者你不知道將多少個字符串連接在一起, StringBuilder
會做得更好。
- 只要它是一個合理的選項選項,將它們全部混合在一個表達式中就更好了。
注意
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
匯編到
string msg = String.Concat("Your total is ", "$500 ");
msg = String.Concat(msg, DateTime.Now.ToString());
每次迭代總計兩個concats和一個ToString。 此外,單個String.Concat非常快,因為它知道結果字符串的大小,因此它只分配結果字符串一次,然后快速將源字符串復制到其中。 這意味着在實踐中
String.Concat(x, y);
永遠都會超越
StringBuilder builder = new StringBuilder();
builder.Append(x);
builder.Append(y);
因為StringBuilder不能使用這樣的快捷方式(你可以調用一個附加的,或者一個刪除,這是String.Concat無法實現的)。
StringBuilder的工作方式是分配一個初始緩沖區並將字符串長度設置為0.對於每個Append,它必須檢查緩沖區,可能分配更多緩沖區空間(通常將舊緩沖區復制到新緩沖區),復制字符串並增加構建器的字符串長度。 String.Concat不需要做所有這些額外的工作。
因此,對於簡單的字符串連接,x + y(即String.Concat)將始終優於StringBuilder。
現在,一旦開始將大量字符串連接到單個緩沖區中,或者你在緩沖區上進行大量操作,你就會開始從StringBuilder中獲益,在不使用StringBuilder時你需要繼續創建新的字符串。 。 這是因為StringBuilder只是偶爾以塊的形式分配新的內存,但String.Concat,String.SubString等(幾乎)總是分配新的內存。 (像“”.SubString(0,0)或String.Concat(“”,“”)之類的東西不會分配內存,但這些都是退化的情況。)
我認為比較String和StringBuilder之間的效率而不是時間更好。
msdn說:String被稱為不可變的,因為它的值一旦創建就無法修改。 看似修改String的方法實際上返回一個包含修改的新String。 如果需要修改類似字符串的對象的實際內容,請使用System.Text.StringBuilder類。
string msg = "Your total is "; // a new string object
msg += "$500 "; // a new string object
msg += DateTime.Now; // a new string object
看哪一個更好。
下面是一個示例,演示了StringBuilder
執行速度比字符串連接更快的情況:
static void Main(string[] args)
{
const int sLen = 30, Loops = 10000;
DateTime sTime, eTime;
int i;
string sSource = new String('X', sLen);
string sDest = "";
//
// Time StringBuilder.
//
for (int times = 0; times < 5; times++)
{
sTime = DateTime.Now;
System.Text.StringBuilder sb = new System.Text.StringBuilder((int)(sLen * Loops * 1.1));
Console.WriteLine("Result # " + (times + 1).ToString());
for (i = 0; i < Loops; i++)
{
sb.Append(sSource);
}
sDest = sb.ToString();
eTime = DateTime.Now;
Console.WriteLine("String Builder took :" + (eTime - sTime).TotalSeconds + " seconds.");
//
// Time string concatenation.
//
sTime = DateTime.Now;
for (i = 0; i < Loops; i++)
{
sDest += sSource;
//Console.WriteLine(i);
}
eTime = DateTime.Now;
Console.WriteLine("Concatenation took : " + (eTime - sTime).TotalSeconds + " seconds.");
Console.WriteLine("\n");
}
//
// Make the console window stay open
// so that you can see the results when running from the IDE.
//
}
結果#1 String Builder占用:0秒。 連接采取:8.7659616秒。
結果#2 String Builder占用:0秒。 連接采取:8.7659616秒。
結果#3 String Builder占用:0秒。 連接采取:8.9378432秒。
結果#4 String Builder占用:0秒。 連接采取:8.7972128秒。
結果#5 String Builder占用:0秒。 連接采取:8.8753408秒。
StringBulder
比+串聯快得多..
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.