【问题标题】:Nested Parallel.ForEach Loops on the same list?在同一个列表上嵌套 Parallel.ForEach 循环?
【发布时间】:2010-07-19 13:46:40
【问题描述】:

我需要并行化一个对列表中的元素进行详尽的成对比较的方法。串行实现很简单:

foreach (var element1 in list)
    foreach (var element2 in list)
        foo(element1, element2);

在这种情况下,foo 不会改变 element1 或 element2 的状态。我知道简单地做嵌套的 Parallel.ForEach 语句是不安全的:

Parallel.ForEach(list, delegate(A element1)
{
    Parallel.ForEach(list, delegate(A element2)
    {
        foo(element1, element2);
    });
});

使用并行任务库实现这一点的理想方法是什么?

【问题讨论】:

    标签: c# parallel-processing task-parallel-library


    【解决方案1】:

    至少如果您是在 内核数至少是列表中项目数的两倍的机器上执行代码,我不确定这样做是否是个好主意嵌入Parallel.ForEachs.

    换句话说,如果您以四核为目标,并且列表有一千个项目,只需并行化父循环即可。并行化两个循环不会使代码更快,而是慢得多,因为并行任务有性能成本。

    alt text http://www.freeimagehosting.net/uploads/ca97f403f8.png

    在每次迭代中,Parallel.ForEach 将丢失几毫秒以确定哪个线程必须执行下一次迭代。假设您有一组 7 个项目。如果并行化父循环,这些毫秒将丢失 7 次。如果将两个循环并行化,它们将丢失 7×7=49 次。设备越大,过热越大。

    【讨论】:

    • 不要假设 PFX 会创建与并行任务一样多的线程 - 它比这更聪明。
    • 当然不是。默认情况下,它会创建与内核一样多的线程。但问题是,每次迭代后,它都会花时间试图找出哪个线程必须执行下一次迭代。
    • 我不认为他是说会有那么多线程,只是为每个函数调用排队一个任务将比仅仅为每个外部循环调用 PFX 引擎有更多的开销.
    • 感谢 MainMa。可能值得注意的是,与 Parallel.ForEach 调用的成本相比, foo 可能需要很长时间。我很欣赏只并行化一个循环的一般论点。
    【解决方案2】:

    你不能只有一个并行和一个正常循环吗?所以要么

    Parallel.ForEach(list, delegate(A element1)
    {
      foreach(列表中的元素 2)
        富(元素1,元素2)
    });

    foreach(列表中的元素1)
    {
      Parallel.ForEach(list, delegate(A element2)
      {
        富(元素1,元素2);
      });
    }

    也应该加快速度。无论如何,每个周期都不会有一个线程,因此这可能与嵌套并行循环一样快或稍慢。

    【讨论】:

      【解决方案3】:

      这两个嵌套循环本质上意味着您想要 foo 列表的笛卡尔积与自身。您可以通过首先在临时列表中创建所有对,然后使用 Parallel.ForEach 迭代该列表来并行化整个操作。

      编辑:您可以使用迭代器返回包含该组合的 2 元素元组,而不是创建所有组合的列表。 Parallel.ForEach 仍将并行处理元组。

      以下示例打印出当前的迭代步骤,以显示结果乱序返回,正如在并行处理期间所预期的那样:

       const int SIZE = 10;
          static void Main(string[] args)
          {
              List<int> list = new List<int>(SIZE);
              for(int i=0;i<SIZE;i++)
              {
                  list.Add(i);
              }
      
      
              Parallel.ForEach(GetCombinations(list),(t,state,l)=>
                  Console.WriteLine("{0},{1},{2}",l,t.Item1,t.Item2));
      
          }
      
          static IEnumerable<Tuple<int,int>> GetCombinations(List<int> list)
          {
              for(int i=0;i<list.Count;i++)
                  for(int j=0;j<list.Count;j++)
                      yield return Tuple.Create(list[i],list[j]);
          }
      

      【讨论】:

      • 你可能不知道 Enumerable.Range()。真的很方便!不过,在代码上,您在这里运行了 3 个循环(而不是 2 个),其中 2 个根本不是并行的。这取决于Foo() 做了什么(Console.WriteLine 在你的回答中),但这可能会比不添加任何并行性并正常循环两次要慢。
      猜你喜欢
      • 2012-09-19
      • 1970-01-01
      • 2020-03-04
      • 1970-01-01
      • 2019-04-22
      • 2019-08-30
      • 2018-01-06
      • 1970-01-01
      • 2018-09-07
      相关资源
      最近更新 更多