【发布时间】:2016-02-25 14:19:20
【问题描述】:
首先,它不是关于在我们开始排序之前具有可能按某种顺序排列的子序列的数组,而是关于特殊结构的数组。
我现在正在编写一个对数据进行排序的简单方法。到目前为止,我使用的是Array.Sort,但PLINQ 的OrderBy 在大型阵列上的性能优于标准Array.Sort。
所以我决定编写自己的多线程排序实现。想法很简单:在分区上拆分一个数组,对每个分区进行并行排序,然后将所有结果合并到一个数组中。
现在我已经完成了分区和排序:
public class PartitionSorter
{
public static void Sort(int[] arr)
{
var ranges = Range.FromArray(arr);
var allDone = new ManualResetEventSlim(false, ranges.Length*2);
int completed = 0;
foreach (var range in ranges)
{
ThreadPool.QueueUserWorkItem(r =>
{
var rr = (Range) r;
Array.Sort(arr, rr.StartIndex, rr.Length);
if (Interlocked.Increment(ref completed) == ranges.Length)
allDone.Set();
}, range);
}
allDone.Wait();
}
}
public class Range
{
public int StartIndex { get; }
public int Length { get; }
public Range(int startIndex, int endIndex)
{
StartIndex = startIndex;
Length = endIndex;
}
public static Range[] FromArray<T>(T[] source)
{
int processorCount = Environment.ProcessorCount;
int partitionLength = (int) (source.Length/(double) processorCount);
var result = new Range[processorCount];
int start = 0;
for (int i = 0; i < result.Length - 1; i++)
{
result[i] = new Range(start, partitionLength);
start += partitionLength;
}
result[result.Length - 1] = new Range(start, source.Length - start);
return result;
}
}
结果我得到一个特殊结构的数组,例如
[1 3 5 | 2 4 7 | 6 8 9]
现在如何使用这些信息并完成排序?插入排序和其他排序不使用块中的数据已经排序的信息,我们只需要将它们合并在一起。我尝试应用来自Merge sort 的一些算法,但失败了。
【问题讨论】:
-
由于您本质上是在进行合并排序,因此您应该继续朝那个方向发展!为什么没有实现 Merge-Sort?
-
@MrPaulch 因为很难实现
in-place合并排序。之前我用了一个qsort,虽然很到位,但是因为随机内存访问,性能比naive单线程差Array.Sort。 -
那么你应该让你的算法适应Quicksort -
Array.Sort实际上使用了 Introsort,它是 Quicksort 和 Heapsort 的混合体 - 我曾经实现了一种高度专业化的快速排序算法,该算法的性能优于Array.Sort,因为它知道必须排序的数据类型。 -
en.wikipedia.org/wiki/Timsort 在部分排序的集合上通常相当快... 算法找到已经排序的数据子集,并使用该知识更有效地对剩余部分进行排序
-
如果一个或多个线程由于某种原因被阻塞,请记住线程数多于内核数会很有用。但是,如果所有线程都处于活动状态,那么由于额外的上下文切换,线程数多于内核数总是会减慢速度。我并不是说你永远不应该这样做。我只是在解释为什么多线程实现会比顺序实现慢。
标签: c# .net arrays algorithm sorting