我需要并行化一个方法,该方法对列表中的元素进行详尽的成对比较。串行实现很简单:
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);
});
});
使用并行任务库实现此目的的理想方法是什么?
答案 0 :(得分:15)
至少如果你在核心数量至少是列表中项目数量的两倍的机器上执行代码,我不确定这是不是一个好主意嵌入式Parallel.ForEach
s。
换句话说,如果您定位四核,并且列表有一千个项,则只需并行化父循环。并行化两个循环不会使代码更快,而是更多,更慢,因为并行任务具有性能成本。
alt text http://www.freeimagehosting.net/uploads/ca97f403f8.png
在每次迭代时,Parallel.ForEach
将丢失几毫秒,以确定哪个线程必须执行下一次迭代。假设您有一组7项。如果并行化父循环,则这些毫秒将丢失7次。如果你并行化两个循环,它们将丢失7×7 = 49次。设备越大,过热就越大。
答案 1 :(得分:11)
难道你不能只有一个平行循环和一个正常循环吗?所以要么
Parallel.ForEach(list, delegate(A element1) { foreach(A element2 in list) foo(element1, element2) });
或
foreach(A element1 in list) { Parallel.ForEach(list, delegate(A element2) { foo(element1, element2); }); }
也应该加快速度。无论如何,每个周期都不会有一个线程,所以这可能与嵌套的并行循环一样快或稍慢。
答案 2 :(得分:1)
这两个嵌套循环本质上意味着你想要 foo 列表的cartessian产品。您可以通过首先在临时列表中创建所有对,然后使用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]);
}