.net大型for循环减慢
本文关键字:循环 for 大型 net | 更新日期: 2023-09-27 17:52:55
我有一个很大的for循环(最大迭代次数为30k),它似乎一直在变慢:
- 前一千次迭代耗时1.34秒
- 经过12k次迭代后,下一个1000次需要5.31秒
- 经过23k次迭代后,下一个1000次需要6.65秒
- 最后一千次迭代耗时7.43秒
为了获得一点性能,我从foreach
循环切换到for
循环,并尝试释放配置,但我在这个问题中找不到适用于我的任何其他内容。循环在一个async方法
为什么循环变慢了?可以避免吗?
for(int iter = 0; iter < LargeList1.Count; iter++)
{
var cl_from = LargeList1[iter];
if(LargeList2.Any(cl => cl.str.Contains(cl_from.str)))
{
DateTime dt1 = //last write time of a file
DateTime dt2 = //last write time of a different file
if(DateTime.Compare(dt1, dt2) > 0)
{
try
{
CopyFile(//Kernel32 CopyFile a file overwrite);
globals.fileX++;
}
catch(Exception filexx)
{
//error handler
}
}
else
{
globals.fileS++;
}
}
else
{
Directory.CreateDirectory(//create a directory, no check if it already exists);
try
{
CopyFile(//Kernel32 CopyFile a file do not overwrite);
globals.fileX++;
}
catch(Exception filex)
{
// error handler
}
}
gui.UpdateCount(globals.fileF, globals.fileX, globals.fileS); //updates iteration on textboxes
float p = (float)100.0*((float)globals.fileF + (float)globals.fileX + (float)globals.fileS)/(float)globals.totalCount;
gui.setProgress(p); //updates progressbar
}
编辑:正如许多人建议的那样,使用hashset.Contains(cl_from.str)解决了这个问题。
我可以想象这两项的性质将成为瓶颈。
for(int iter = 0; iter < LargeList1.Count; iter++)
{
.....
if(LargeList2.Any(cl => cl.str.Contains(cl_from.str)))
...........
您正在检查当前字符串中是否包含另一个大列表中的单词。
随着时间的推移,它可能会变慢的几个原因:
- 最初它更快,因为GC没有运行那么多,随着循环的深入,GC必须收集越来越频繁。
- 字符串
cl_from.st
的长度可能越来越大?
需要考虑的几点:
cl_from.str
和LargeList2
有多大,是否值得在cl_from.str
中创建所有可能值的哈希,然后检查是否有查找,甚至可能创建所有LargeList2字符串的哈希集,然后使用它,迭代cl_From.str
中的每个字符串组合。你可能想要改进你的搜索算法,例如,查看c# . net:最快的方法来检查字符串是否在字符串中出现。或者谷歌其他字符串搜索索引/算法。为什么不使用Lucene.NET之类的东西呢?
使用。net分析器找出瓶颈在哪里,以及它在哪里花费时间。
另一种可能是文件系统给您带来了麻烦。如果在一个文件夹中有数千个文件,打开一个文件可能会花费很长时间。系统加载目录并进行顺序搜索以找到您所请求的文件的条目。
如果您获得目录中的文件列表,然后逐个打开它们,那么随着您深入列表,打开文件所需的时间将越来越长。如果有,例如:
foreach (var filename in Directory.GetFiles(...))
{
// start stopwatch
// open the file
// stop stopwatch
// display time
// close the file
}
您会发现打开文件的时间随着文件列表的增加而增加。当你谈论几百个文件时,这种差异并不明显,但当你在一个文件夹中有10,000个文件时,这种差异就变得非常明显了。
解决方案是把东西分开,这样你就不会在一个文件夹里有那么多文件。与其将10,000个文件放在一个文件夹中,不如创建100个文件夹,每个文件夹包含100个文件。或者10个文件夹,每个文件夹1000个文件。这两种方法都比保存大量文件的单个文件夹快得多。
我敢肯定违规行会是:
if(LargeList2.Any(cl => cl.str.Contains(cl_from.str)))
由于匿名委托的开销,带有lambda表达式的'Any'扩展方法最终将比长手写版本慢,因此您可以尝试展开如下:
foreach (var cl in LargeList2)
{
if (cl.str.Contains(cl_from.str))
{
// do stuff
break;
}
}
但我认为这个迭代是你可能会发现缓慢的地方,与匿名委托和字符串函数。随着迭代的进行,很可能迭代必须遍历大部分的LargeList2才能找到匹配,这意味着随着过程的进行,性能会减慢。
尝试使用启用了行级计时的ANTS性能分析器,看看在运行该进程后,它是否将这一行显示为热点。