.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)解决了这个问题。

.net大型for循环减慢

我可以想象这两项的性质将成为瓶颈。

for(int iter = 0; iter < LargeList1.Count; iter++)
{
    .....
    if(LargeList2.Any(cl => cl.str.Contains(cl_from.str)))
    ...........

您正在检查当前字符串中是否包含另一个大列表中的单词。

随着时间的推移,它可能会变慢的几个原因:

  1. 最初它更快,因为GC没有运行那么多,随着循环的深入,GC必须收集越来越频繁。
  2. 字符串cl_from.st的长度可能越来越大?

需要考虑的几点:

  1. cl_from.strLargeList2有多大,是否值得在cl_from.str中创建所有可能值的哈希,然后检查是否有查找,甚至可能创建所有LargeList2字符串的哈希集,然后使用它,迭代cl_From.str中的每个字符串组合。

  2. 你可能想要改进你的搜索算法,例如,查看c# . net:最快的方法来检查字符串是否在字符串中出现。或者谷歌其他字符串搜索索引/算法。为什么不使用Lucene.NET之类的东西呢?

  3. 使用。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性能分析器,看看在运行该进程后,它是否将这一行显示为热点。