我们经常在编程时需要对数组进行排序。为此,我们在数组类(即sort())中使用Java提供的内置方法。 sort()方法使用合并排序或Tim排序对数组元素进行排序。在这两种情况下,sort()方法都会对数组的元素进行顺序排序。在Java 8中,引入了用于排序的新API,即并行排序。
并行排序使用Java 7中引入的Fork / Join框架将排序任务分配给线程池中可用的多个线程。 Fork / Join实现了一种工作窃取算法,该算法在空闲线程中可以窃取在另一个线程中排队的任务。
例如,下面的代码是一个使用Arrays.sort()和Arrays.parallelSort()对一个双精度随机数组进行排序的程序。该程序仅测量这两种方法之间的性能差异。 :
// Java program to demonstrate time taken by sort()
// and parallelSort() methods.
import java.util.Arrays;
public class ParallelSortTest
{
private static final int BASE_ARRAY_SIZE = 10000;
// A utility function to generate and return an
// an array of given size filled with randomly
// generated elements.
public static double[] generateArray(int size)
{
if (size <= 0 || size > Integer.MAX_VALUE)
return null;
double[] result = new double[size];
for (int i = 0; i < size; i++)
result[i] = Math.random();
return result;
}
// Driver code to compare two sortings
public static void main(String[] args)
{
for (int i = 1; i < 10000; i *= 10)
{
int size = BASE_ARRAY_SIZE * i;
double[] arr1 = generateArray(size);
// Creating a copy of arr1 so that we can
// use same content for both sortings.
double[] arr2 = Arrays.copyOf(arr1, arr1.length);
System.out.println("Array Size: " + size);
// Sorting arr1[] using serial sort
long startTime = System.currentTimeMillis();
Arrays.sort(arr1);
long endTime = System.currentTimeMillis();
System.out.println("Time take in serial: " +
(endTime - startTime) + "ms.");
// Sorting arr2[] using serial sort
startTime = System.currentTimeMillis();
Arrays.parallelSort(arr2);
endTime = System.currentTimeMillis();
System.out.println("Time take in parallel: "
+ (endTime - startTime) + "ms.");
System.out.println();
}
}
}
环境 :
2.6 GHz Intel Core i7
java version "1.8.0_25"
注意:由于数组中的随机值,所需时间可能会有所不同。
两种算法之间的主要区别如下:
1) Arrays.sort() :是一种顺序排序。
- API使用单线程进行操作。
- 执行该操作需要更长的时间。
2. Arrays.ParallelSort():是并行排序。
- 该API使用多个线程进行操作。
- 当有很多元素时,它会更快,而对于更少的元素,它会更慢。
分析 :
结果表明,在多核计算机上并行排序可以在100万个或更多元素上实现性能提升。当低于此阈值时,它实际上可能比顺序排序要慢。此结果符合预期,此处合适的大小可能为100万。您的里程可能会有所不同,这取决于您的环境。
解释 :
现在,让我们看一下代码以弄清楚这种并行排序是如何工作的。
public static void parallelSort(double[] a) {
int n = a.length, p, g;
if (n <= MIN_ARRAY_SORT_GRAN ||
(p = ForkJoinPool.getCommonPoolParallelism()) == 1)
DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);
else
new ArraysParallelSortHelpers.FJDouble.Sorter
(null, a, new double[n], 0, n, 0,
((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
MIN_ARRAY_SORT_GRAN : g).invoke();
}
如我们所见,存在最小粒度(Java.util.Arrays.MIN_ARRAY_SORT_GRAN = 8192 [0x2000]),如果数组的长度小于最小粒度,则使用DualPivotQuicksort.sort而不是直接对其进行排序排序任务分区。通常,使用较小的大小会导致跨任务的内存争用,这使得并行加速的可能性不大。另一个值得注意的判断是ForkJoinPool.getCommonPoolParallelism(),它返回公共池的目标并行度(默认情况下,等于可用处理器Runtime.getRuntime()。availableProcessors()的数量)。而且,如果您的计算机只有1个工作线程,则也不会使用并行任务。当数组长度达到最小粒度并且您拥有1个以上的工作线程时,将使用并行排序方法对数组进行排序。 ForkJoin公共池在此处用于执行并行任务。
参考 :
http://下载。 Java.net / lambda / b84 / docs / api / Java/util/Arrays.html#parallelSort%28int