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

C# 嵌套循环和字符串连接的性能问题,c#,.net,string,performance,C#,.net,String,Performance,有人能解释一下为什么该代码运行时间如此之长,即>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 =

有人能解释一下为什么该代码运行时间如此之长,即>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 framework中,字符串是不可变的,这意味着它不能被就地修改。这意味着+=运算符每次都必须创建一个新字符串,这意味着分配内存、复制现有字符串的值并将其写入新位置。一个或两个串联是可以的,但是一旦你把它放入一个循环中,你就需要使用一个替代方案

通过使用以下代码,您将看到巨大的性能改进:

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是一个字符串,则应改用StringBuilder。字符串是不可变的,添加小部分非常无效

理想情况下,您应该使用合理的大小初始化StringBuilder。请参见。

使用一个而不是几百万个连接

如果连接两个字符串,这意味着系统将分配新内存以包含这两个字符串,然后在中复制这两个字符串。大量的大内存分配和复制操作变得非常缓慢


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

假设矩阵的大小为MxM,并且有N个元素。您正在以迭代次数为^2或OM^4的方式构建字符串。每个操作都必须复制已经存在的内容。问题不在于像临时字符串这样的常量因素开销

使用StringBuilder


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

我看到的最大问题是您将textToWrite用作字符串

由于字符串是不可变的,因此每次更改字符串时,必须保留新内存,并从以前的版本复制

一种更有效的方法是使用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 to 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();

运行时间如此之长的原因是您正在使用字符串连接来创建字符串。对于每一次迭代,它都会将整个字符串复制到一个新字符串中,因此最终您将复制的字符串的总和是最终字符串的几百万倍

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

最有可能是因为您使用每个textToWrite+=随便什么创建了巨大的字符串。在Java中,可以使用StringBuilder高效地创建大字符串,尝试在C中找到等效的字符串。谢谢大家!显然,他似乎做到了这一点。我会将答案标记为我用来帮助我解决问题的代码。我以前使用过StringBuilder,但时间不长,我也不知道为什么。现在我知道了!!!请解释为什么它没有效率,如果他能够计算缓冲区的最终大小,他如何分配缓冲区?如果级联效率较低,C编译器将始终使用StringBuilder。有时串联效率较低。恒定因素管理费用不是问题所在。问题是二次运行时间。@BlueTrin类字符串是不可变的,因此每次在内存中创建新对象时。这就是使用的原因stringbuilder@NitinVarpe当前位置我知道答案,我希望他编辑他的答案。临时问题不是问题。问题是二次运行时间。有没有办法避免枚举所有矩阵元素的算法在^2上?在我的示例中,N是串联数。如果将M作为矩阵维数,则此算法为OM^4。谢谢。我认为n是行/列的数量。您的代码实际上更好-您使用了删除最后一个逗号的正确实现