C#中的并行分区算法:如何最大化并行性

时间:2012-04-06 21:49:38

标签: c# parallel-processing plinq data-partitioning

我在C#中编写了一个并行算法,将一个数组分成两个列表,一个包含满足给定谓词的元素,另一个列表包含无法满足谓词的元素。这是一种订单保留算法。

我写的如下,但我想知道如何最大化从硬件并发中获利的机会。

    static void TestPLinqPartition(int cnt = 1000000)
    {
        Console.WriteLine("PLINQ Partition");
        var a = RandomSequenceOfValuesLessThan100(cnt).ToArray();
        var sw = new Stopwatch();
        sw.Start();
        var ap = a.AsParallel();
        List<int> partA = null;
        List<int> partB = null;
        Action actionA = () => { partA = (from x in ap where x < 25 select x).ToList(); };
        Action actionB = () => { partB = (from x in ap where !(x < 25) select x).ToList(); };
        Parallel.Invoke(actionA, actionB);
        sw.Stop();

        Console.WriteLine("Partion sizes = {0} and {1}", partA.Count, partB.Count);
        Console.WriteLine("Time elapsed = {0} msec", sw.ElapsedMilliseconds);
    }

2 个答案:

答案 0 :(得分:4)

如果你的列表很长,你将无法获得更多的并行性(2x)。相反,我建议使用Parallel.For并使用线程本地Tuple<List<int>, List<int>>作为并行循环状态。 Parallel.For API允许您轻松完成此操作。您可以在最后合并各个子列表。

这个版本令人尴尬地并行,并且因为没有同步而导致CPU总线上几乎没有一致性流量。

编辑:我想强调一下,你不能只使用所有线程共享的两个List,因为这会导致疯狂的同步开销。您需要使用线程本地列表。即使ConcurrentQueue也不适用于这种情况,因为它使用Interlocked操作,这会导致CPU一致性流量受限。

答案 1 :(得分:1)

我将数据分成小段(例如使用Partitioner类),并为每个分区分配与其位置相关的索引。对于每个编号的分区,我创建一个Task,将分区分成两组,一组与谓词匹配,另一组不与并返回两组,以及它们源自的分区的索引。 ,作为Task的返回值。然后我等待所有任务完成,然后.Concat()(以防止浪费时间实际合并所有数据)匹配组根据其索引,对于不匹配组则相同。您应该能够以这种方式实现任意程度的并行性,同时保留相对项目顺序。