multithreadingquicksort或mergesort

我怎样才能为Java实现一个并发的quicksort或mergesortalgorithm?

我们在16位(虚拟)核心Mac上遇到问题,其中只有一个核心(!)正在使用默认的Javasortingalgorithm,而且很好地看到非常好的机器被完全没有使用。 所以我们写了我们自己的(我写的),而且确实获得了很好的加速(我写了一个multithreading的快速sorting,由于它的分区性质,它并行化很好,但我也可以写一个mergesort)多达4个线程,它是专有代码,我宁愿使用来自信誉良好的源代码,而不是使用我重新发明的轮子。

我在网上find的唯一一个例子是如何不用 Java编写multithreading的快速sorting,它是繁忙循环(这真的很糟糕)使用:

while (helpRequested) { } 

http://broadcast.oreilly.com/2009/06/may-column-multithreaded-algor.html

所以除了失去一个线程没有理由,它确保通过在while循环(这是mindboggling)忙循环杀死perfs。

因此,我的问题是:你知道任何正确的multithreading的快速sorting或Java中的合并实现将从一个有信誉的来源?

我把重点放在这样一个事实上,即我知道复杂性保持O(n log n),但是我仍然非常乐意看到所有这些内核开始工作,而不是闲置。 请注意,对于其他任务,在同样的16个虚拟内核Mac上,我通过并行化代码(我并不是指并发专家)加速到x7。

所以,即使艰难的复杂性保持O(n日志n),我真的很感激x7或x8甚至x16的加速。

尝试Doug Lea的fork / join框架 :

 public class MergeSort extends RecursiveAction { final int[] numbers; final int startPos, endPos; final int[] result; private void merge(MergeSort left, MergeSort right) { int i=0, leftPos=0, rightPos=0, leftSize = left.size(), rightSize = right.size(); while (leftPos < leftSize && rightPos < rightSize) result[i++] = (left.result[leftPos] <= right.result[rightPos]) ? left.result[leftPos++] : right.result[rightPos++]; while (leftPos < leftSize) result[i++] = left.result[leftPos++]; while (rightPos < rightSize) result[i++] = right.result[rightPos++]; } public int size() { return endPos-startPos; } protected void compute() { if (size() < SEQUENTIAL_THRESHOLD) { System.arraycopy(numbers, startPos, result, 0, size()); Arrays.sort(result, 0, size()); } else { int midpoint = size() / 2; MergeSort left = new MergeSort(numbers, startPos, startPos+midpoint); MergeSort right = new MergeSort(numbers, startPos+midpoint, endPos); coInvoke(left, right); merge(left, right); } } } 

(来源: http : //www.ibm.com/developerworks/java/library/j-jtp03048.html?S_TACT=105AGX01&S_CMP=LP )

对不起,但你要求的是不可能的。 我相信别人提到sorting是IO界限,他们很可能是正确的。 来自IBM的Doug Lea的代码是一个很好的工作,但是我相信它主要是作为一个如何编写代码的例子。 如果你在他的文章中注意到,他从来没有发布基准testing,而是发布了其他工作代码的基准,例如计算平均值和并行查找最小值。 如果您使用通用合并sorting,快速sorting,使用join分叉池的双合并sorting,以及使用快速sortingjoin分叉池编写的基准,以下是testing的基准。 你会看到合并sorting是100或更less的N的最佳。 快速sorting1000到10000,如果你有100000或更高,使用联合叉池的快速sorting比其他的快。 这些testing是运行30次的随机数组来创build每个数据点的平均值,并且运行在具有大约2个RAM的四核上。 在下面我有快速sorting的代码。 这大多表明,除非你想sorting一个非常大的数组,你应该退出尝试改进你的代码sortingalgorithm,因为并行的在小N上运行速度非常慢。

 Merge Sort 10 7.51E-06 100 1.34E-04 1000 0.003286269 10000 0.023988694 100000 0.022994328 1000000 0.329776132 Quick Sort 5.13E-05 1.60E-04 7.20E-04 9.61E-04 0.01949271 0.32528383 Merge TP 1.87E-04 6.41E-04 0.003704411 0.014830678 0.019474009 0.19581768 Quick TP 2.28E-04 4.40E-04 0.002716065 0.003115251 0.014046681 0.157845389 import jsr166y.ForkJoinPool; import jsr166y.RecursiveAction; // derived from // http://www.cs.princeton.edu/introcs/42sort/QuickSort.java.html // Copyright © 2007, Robert Sedgewick and Kevin Wayne. // Modified for Join Fork by me hastily. public class QuickSort { Comparable array[]; static int limiter = 10000; public QuickSort(Comparable array[]) { this.array = array; } public void sort(ForkJoinPool pool) { RecursiveAction start = new Partition(0, array.length - 1); pool.invoke(start); } class Partition extends RecursiveAction { int left; int right; Partition(int left, int right) { this.left = left; this.right = right; } public int size() { return right - left; } @SuppressWarnings("empty-statement") //void partitionTask(int left, int right) { protected void compute() { int i = left, j = right; Comparable tmp; Comparable pivot = array[(left + right) / 2]; while (i <= j) { while (array[i].compareTo(pivot) < 0) { i++; } while (array[j].compareTo(pivot) > 0) { j--; } if (i <= j) { tmp = array[i]; array[i] = array[j]; array[j] = tmp; i++; j--; } } Partition leftTask = null; Partition rightTask = null; if (left < i - 1) { leftTask = new Partition(left, i - 1); } if (i < right) { rightTask = new Partition(i, right); } if (size() > limiter) { if (leftTask != null && rightTask != null) { invokeAll(leftTask, rightTask); } else if (leftTask != null) { invokeAll(leftTask); } else if (rightTask != null) { invokeAll(rightTask); } }else{ if (leftTask != null) { leftTask.compute(); } if (rightTask != null) { rightTask.compute(); } } } } } 

Java 8提供了java.util.Arrays.parallelSort ,它使用fork-join框架并行排列数组。 文档提供了一些关于当前实现的细节(但是这些是不规范的注释):

sortingalgorithm是一个并行sorting合并,将数组分解成自己sorting然后合并的子数组。 当子数组长度达到最小粒度时,使用适当的Arrays.sort方法对子数组进行sorting。 如果指定数组的长度小于最小粒度,则使用适当的Arrays.sort方法对其进行sorting。 该algorithm需要一个不大于原始数组大小的工作空间。 ForkJoin公共池用于执行任何并行任务。

似乎没有相应的列表并行sorting方法(即使RandomAccess列表应该sorting很好),所以您需要使用toArray ,sorting该数组,并将结果存储回列表中。 (我在这里问了一个关于这个的问题。)

刚刚编码了上面的MergeSort和性能非常差。

代码块引用“coInvoke(左,右);” 但没有提及这个,并用invokeAll(左,右)取代了它;

testing代码是:

 MergeSort mysort = new MyMergeSort(array,0,array.length); ForkJoinPool threadPool = new ForkJoinPool(); threadPool.invoke(mysort); 

但由于performance不佳,不得不阻止它。

我看到上面的文章已经快一年了,也许事情现在已经改变了。

我发现替代文章中的代码可以工作: http : //blog.quibb.org/2010/03/jsr-166-the-java-forkjoin-framework/

您可能确实考虑过这个问题,但是从更高级别来看具体问题可能会有所帮助,例如,如果您不sorting一个数组或列表,使用传统algorithm可以更简单地对各个集合进行sorting,而不是试图同时对一个集合进行sorting。

过去几天我一直在面对multithreading的sorting问题。 就像在这个加州理工大学上所做的解释一样,只要简单地multithreading处理除了明显的线程数(分割数)之外的分而治之的方法是有限的。 我想这是因为虽然你可以使用你的机器的所有64核心在64个线程上运行64个分区,但是4个分区只能运行在4个线程,2对2和1对1等等上。recursion的机器利用率低下。

昨天晚上我发现了一个解决scheme,这对我自己的工作可能有用,所以我会在这里发布。

Iff,你的sorting函数的第一个标准是基于一个整数的最大大小s,它是一个实际的整数或一个string中的字符,这样的整数或字符完全定义最高级别的sorting,那么我认为一个非常快速(和简单)的解决scheme。 只需使用该初始整数将sorting问题分为较小的sorting问题,然后使用您select的标准单线程sortingalgorithm进行sorting。 我想可以一次性完成分class。 在做独立分类之后,没有合并的问题,因为你已经知道第一课的所有东西都在第二课之前sorting,依此类推。

例如:如果您希望基于strcmp()进行sorting,则可以使用string中的第一个字符将数据分为256个类,然后在下一个可用线程中对每个类进行sorting,直到全部完成。

这个方法充分利用了所有可用的内核,直到问题解决,我认为这很容易实现。 我还没有实现,所以可能有问题,我还没有find。 它显然不能用于浮点types,并且对于大型s来说效率不高。 其性能也将严重依赖于用于定义类的整数/字符的熵。

这可能是费边·斯蒂格用更less的话来build议的,但是我明确地说,在某些情况下,你可以从更大的sorting中创build多个更小的sorting。

你为什么认为平行sorting会有帮助? 我认为大多数sorting是I / O界限,而不是处理。 除非你的比较做了很多计算,否则加速是不太可能的。