嵌套循环和字符串连接的性能问题

时间:2014-01-14 09:53:31

标签: c# .net string performance

有人可以解释为什么这段代码运行这么长时间(即> 24小时): 行数为5000,而列数为2000(即大约10米的循环)。

有更好的方法吗????

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;
}

8 个答案:

答案 0 :(得分:6)

是的,+=运营商效率不高。请改用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();

答案 1 :(得分:2)

我看到的最大问题是你将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();
}

答案 2 :(得分:2)

你的代码花了这么长时间,因为你要附加字符串,随时创建数千个新的临时字符串。内存管理器需要为这些字符串找到内存(这会增加内存需求,因为它们会变长),并且操作会将到目前为止的字符(每次迭代的次数增加)复制到最新的字符串。

替代方法是使用单个StringBuilder,您可以在其上调用Append()以更有效地追加,最后,ToString()当您完成获取最终的字符串时想用。

答案 3 :(得分:2)

因为你创造了大量的字符串。

你应该使用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();

答案 4 :(得分:1)

假设textToWriteString,您应该使用StringBuilder代替StringStringBuilder是不可变的,添加小部件非常无效。

理想情况下,您会以合理的尺寸初始化{{1}}(请参阅doc)。

答案 5 :(得分:1)

使用StringBuilder代替数百万个连接。

如果连接2个字符串,这意味着系统会分配新的内存来包含它们,然后将它们都复制进去。很多大内存分配和复制操作变得非常快。

StringBuilder所做的是通过分配'提前'来极大地减少这种情况,因此只需要将缓冲区增长几次并将其复制进去,从而消除循环中最慢的因素。

答案 6 :(得分:1)

假设矩阵的大小为MxM且具有N个元素。您正在以一种在迭代次数中采用 O(N^2) (或O(M^4))的方式构建字符串。每个操作必须复制已存在的内容。问题是某些常量因素开销,如临时字符串。

使用StringBuilder。

字符串连接对于少量连接字符串更有效。对于动态数量的字符串,请使用StringBuilder。

答案 7 :(得分:0)

运行这么长时间的原因是因为您使用字符串连接来创建字符串。对于每次迭代,它会将整个字符串复制到一个新字符串,因此最后您将复制的字符串最多可以添加到最终字符串的数百万倍。

使用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();
}