[英]Performance issues with nested loops and string concatenations
有人可以解釋一下為什么這段代碼要花這么長時間才能運行(即> 24小時):行數是5000,而列數是2000(即約10m循環)。
有一個更好的方法嗎????
for (int i = 0; i < m.rows; i++)
{
for (int j = 0; j < m.cols; j++)
{
textToWrite += m[i, j].ToString() + ",";
}
//remove the final comma.
textToWrite = textToWrite.Substring(0,textToWrite.Length-2);
textToWrite += Environment.NewLine;
}
是的, +=
運算符不是很有效。 請改用StringBuilder
。
在.NET框架中,字符串是不可變的,這意味着無法就地對其進行修改。 這意味着+=
運算符每次都必須創建一個新字符串,這意味着分配內存,復制現有字符串的值並將其寫入新位置。 一兩個串聯是可以的,但是一旦將其放入循環中,就需要使用替代方法。
http://support.microsoft.com/kb/306822
通過使用以下代碼,您將看到巨大的性能提升:
var textToWriteBuilder = new StringBuilder();
for (int i = 0; i < m.rows; i++)
{
for (int j = 0; j < m.cols; j++)
{
textToWriteBuilder.Append(m[i, j].ToString() + ",");
}
// I've modified the logic on the following line, I assume you want to
// concatenate the value instead of overwriting it as you do in your question.
textToWriteBuilder.Append(textToWriteBuilder.Substring(0, textToWriteBuilder.Length - 2));
textToWriteBuilder.Append(Environment.NewLine);
}
string textToWrite = textToWriteBuilder.ToString();
我看到的最大問題是您將textToWrite用作string
。
由於字符串是不可變的,因此每次更改字符串時,都必須保留從先前版本復制的新內存。
一種更有效的方法是使用專門針對此類情況設計的StringBuilder
類。 例如:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < m.rows; i++)
{
for (int j = 0; j < m.cols; j++)
{
sb.Append(m[i, j].ToString());
if(j < m.cols - 1) // don't add a comma on the last element
{
sb.Append(",");
}
}
sb.AppendLine();
}
您的代碼花費了很長時間,因為您要追加字符串,並在運行時創建數千個新的臨時字符串。 內存管理器需要為這些字符串找到內存(隨着時間的增加,內存需求會增加),並且該操作會將您到目前為止擁有的字符(每次迭代的數量都會增加)復制到最新的字符串中。
另一種方法是使用單個StringBuilder
,在其上調用Append()
可以更高效地附加,最后,在完成獲取要使用的定型字符串后,可以調用ToString()
。
因為您正在創建大量的字符串。
您應該為此使用StringBuilder。
StringBuilder sb = new StringBuildeR();
for (int i = 0; i < m.rows; i++)
{
bool first = true;
for (int j = 0; j < m.cols; j++)
{
sb.Append(m[i, j]);
if (first)
{
first = false;
}
else
{
sb.Append(",");
}
}
sb.AppendLine();
}
string output = sb.ToString();
假設textToWrite
是一個String
,則應該改用StringBuilder
。 String
是不可變的,添加小部分效果不佳。
理想情況下,您將使用合理的大小初始化StringBuilder
(請參閱doc )。
使用StringBuilder
而不是數百萬個串聯。
如果連接2個字符串,則意味着系統分配新的內存以包含兩個字符串,然后將它們都復制進去。成千上萬的大內存分配和復制動作變得非常慢。
StringBuilder
所做的是通過“預先分配”來極大地減少這種情況,因此只需將緩沖區增加幾次並復制就可以了,從而消除了循環中最慢的因素。
假設矩陣的大小為MxM,並且具有N個元素。 您正在以一種在迭代次數中采用O(N^2)
(或O(M^4)
)的方式構建字符串。 每個操作都必須復制已經存在的內容。 問題不是像臨時字符串這樣的常量因素開銷。
使用StringBuilder。
字符串串聯對於少量的串聯字符串更有效。 對於動態數量的字符串,請使用StringBuilder。
運行時間如此之久的原因是,您正在使用字符串連接來創建字符串。 對於每次迭代,它將整個字符串復制到一個新字符串,因此最終,您將復制的字符串加起來是最終字符串的幾百萬倍。
使用StringBuilder
創建字符串:
StringBuilder textToWrite = new StringBuilder();
for (int i = 0; i < m.rows; i++)
{
for (int j = 0; j < m.cols; j++)
{
if (j > 0) textToWrite.Append(',');
textToWrite.Append(m[i, j]);
}
textToWrite.AppendLine();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.