如何提高许多foreach循环的性能?

时间:2016-03-31 11:34:27

标签: c# performance linq

private void GenerateRecords(JobRequest request)
{
     for (var day = 0; day < daysInRange; day++)
     {
          foreach (var coreId in request.CoreIds)
          {
               foreach (var agentId in Enumerable.Range(0, request.AgentsCount).Select(x => Guid.NewGuid()))
               {
                  for (var copiesDone = 0; copiesDone < request.CopiesToMake; copiesDone++)
                  {
                       foreach (var jobInfoRecord in request.Jobs)
                       {
                             foreach (var status in request.Statuses)
                             {
                                    //DoSomeWork();
                             }
                       }
                   }
               }
          }
     } 
}

有没有办法提高多次迭代的性能?我真的需要拥有所有这些循环,但我想知道如何改进(加速)迭代?也许使用linq?

3 个答案:

答案 0 :(得分:4)

一旦你放弃了LINQ,你就会受到收集枚举器和垃圾收集器的支配。

如果要从foreach循环中尽可能多地挤出性能并控制数据结构,请确保使用具有结构枚举器的集合类型(即List<T>,{ {1}})。更好的是,尽可能使用普通的通用数组。尽管是非结构枚举器,但它们在原始访问速度方面是.NET中最快的集合类型,至少在Release /中构建时启用了优化(在这种情况下,编译器为您的{{1}发出相同的IL }}循环遍历数组,就像ImmutableArray<T>循环一样,从而减少通常与使用实现foreach的类型相关联的分配和方法调用。)

Roslyn有set of guidelines个热门代码路径,在您的情况下非常有用:

  
      
  • 避免使用LINQ。
  •   
  • 避免在没有结构枚举器的集合上使用foreach。
  •   

现在,上述内容在任何性能关键情况下都有效。但是,我有点怀疑收集迭代性能是您特定情况下的瓶颈。 for花费的时间比你想要的要长得多。您应该对IEnumerable<T>方法调用进行概要分析,以获得关于哪些代码需要最多关注的明确答案。

如果您认为DoSomeWork实施是最佳的,请考虑并行化您的工作量。

如果您的GenerateRecords实现是纯粹的并且不依赖于外部可变状态(即类变量),那么您可以通过DoSomeWork或{{1}并行化一些循环迭代。 }。你最外面的循环看起来像是一个特别好的候选者,但你可能不得不放置并行循环,直到你获得所需的性能特征。作为一个起点,我建议:

DoSomeWork

答案 1 :(得分:1)

您需要做多少次//DoSomeWork()? 这些浪费了吗? 如果没有,那么循环并不重要。

您可以说,在Visual Studio IDE下运行它,并且在它运行时,点击&#34; Pause&#34;按钮。 然后显示调用堆栈。

除非//DoSomeWork()少于几条指令,否则暂停将落在//DoSomeWork()。 如果这样做10次,那么落在该函数中的样本分数大约是它花费的时间的一小部分。 如果10个样本中有8个落在该函数中,并且其中2个落在循环中(很可能是最里面的循环),那么即使您展开循环或将其成本降低到0,您也不会节省超过约20%。

花费精力的是最重要的成本。

答案 2 :(得分:1)

与旧学校相比,LINQ确实没有加速任何事情。 for循环。相反,LINQ通常成本很低。但是,根据您的问题(您应该测量以更好地理解它),您可以通过并行化解决方案来提高性能。在这里LINQ可以很方便。

您可以将所有for循环更改为使用LINQ创建的单个IEnumerable<T>

var query = from day in Enumerable.Range(0, request.daysInRange)
            from coreId in request.CoreIds
            from agentId in Enumerable.Range(0, request.AgentsCount).Select(x => Guid.NewGuid())
            from copiesDone in Enumerable.Range(0, request.CopiesToMake)
            from jobInfoRecord in request.Jobs
            from status in request.Statuses
            select new {
              Day = day,
              CoreId = coreId,
              AgentId = agentId,
              CopiesDone = copiesDone,
              JobInfoRecord = jobInfoRecord,
              Status = status
            };

假设你在循环中所做的工作是创建一个新对象,你可以使用Select来投射项目:

var results = query.Select(item => /* do some work and return something */);

但是,您可以使用PLINQ来并行化代码,并在插入AsParallel()时略有变化:

var results = query.AsParallel().Select(item => /* do some work and return something */);

如果你有一个4核计算器,只要正在完成的工作是CPU限制的,你就可以期望速度提高近4倍。

调试并行代码可能很困难,但假设您调试的任何问题与并行执行的代码无关,您可以通过删除.AsParallel()轻松关闭并行化。这可能非常方便。