📜  Java的串行排序与并行排序

📅  最后修改于: 2021-04-24 19:59:30             🧑  作者: Mango

我们经常在编程时需要对数组进行排序。为此,我们在数组类(即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