簡體   English   中英

嵌套循環和字符串連接的性能問題

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM