C# 嵌套循环和字符串连接的性能问题
有人能解释一下为什么该代码运行时间如此之长,即>24小时: 行数为5000,列数为2000,即大约10m的循环 有更好的方法吗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 =
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是行/列的数量。您的代码实际上更好-您使用了删除最后一个逗号的正确实现