.net大的循环减慢

时间:2015-06-02 20:44:45

标签: c# .net asynchronous

我有一个大的for循环(最大30k迭代)似乎一直在减速:

  • 前1000次迭代需要1.34s
  • 经过12k次迭代后,下一千次采取5.31s
  • 经过23k次迭代后,下一千次采用6.65s
  • 最后一千次迭代需要7.43s

为了获得一点性能,我从foreach循环切换到for循环,并尝试了发布配置,但我在this question中找不到任何适用的内容对我来说。循环采用异步方法

为什么循环减慢?可以避免吗?

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

3 个答案:

答案 0 :(得分:3)

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

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的长度可能会变大?
  3. 需要考虑的一些要点:

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

    2. 您可能希望改进搜索算法,例如看看C# .NET: FASTEST WAY TO CHECK IF A STRING OCCURS WITHIN A STRING。或谷歌搜索其他字符串搜索索引/算法。为什么不使用像Lucene.NET这样的东西?

    3. 使用.NET分析器查找瓶颈所在的位置以及花费时间的位置。

答案 1 :(得分:1)

另一种可能性是文件系统导致您遇到麻烦。如果单个文件夹中有数千个文件,则打开文件可能需要很长时间。系统加载目录并执行顺序搜索以查找所请求文件的条目。

如果您正在获取目录中的文件列表,然后逐个打开它们,那么当您进一步进入列表时,打开文件将花费越来越长的时间。如果你有,例如:

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个文件夹,每个文件夹1,000个文件要么比具有大量文件的单个文件夹快得多。

答案 2 :(得分:0)

我很确定违规行将是:

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性能分析器,以查看在运行此过程后它是否将此行显示为热点。