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

Ana*_*oly -1 c# linq performance

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();
                             }
                       }
                   }
               }
          }
     } 
}
Run Code Online (Sandbox Code Playgroud)

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

Kir*_*kiy 5

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

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

Roslyn 有一热代码路径指南,这些指南在您的情况下很有用:

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

现在,以上内容在任何性能关键的场景中都是有效的。但是,我有点怀疑集合迭代性能是您特定情况下的瓶颈。这是更有可能的是DoSomeWork花费的时间比你想。您应该分析您的GenerateRecords方法调用以获得关于哪些代码位最需要关注的明确答案。

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

如果您的DoSomeWork实现是纯粹的并且不依赖于外部可变状态(即类变量),您就可以通过Parallel.For或并行化您的某些循环迭代Parallel.ForEach。您的最外层循环看起来是一个特别好的候选者,但您可能必须尝试放置并行循环,直到获得所需的性能特征。作为起点,我建议:

private void GenerateRecords(JobRequest request)
{
    Parallel.For(0, daysInRange, day =>
    {
        foreach (var coreId in request.CoreIds)
        {
            for (var i = 0; i < request.AgentsCount; i++)
            {
                var agentId = Guid.NewGuid();

                for (var copiesDone = 0; copiesDone < request.CopiesToMake; copiesDone++)
                {
                    foreach (var jobInfoRecord in request.Jobs)
                    {
                        foreach (var status in request.Statuses)
                        {
                            //DoSomeWork();
                        }
                    }
                }
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)