Ошибка нехватки памяти при распараллеливании сортировки слиянием - PullRequest
1 голос
/ 07 мая 2011

Я пытаюсь parallelize мой сортировать слияния реализация: http://pastebin.com/2uMGjTxr. Я хочу создать столько потоков, сколько может обеспечить Java-VM. Я хочу определить максимальное количество возможных потоков, используя java.lang.Runtime .

Итак, я придумал класс с именем MergeThread:

public class MergeThread implements Runnable{

    public int[] list;
    int sIndex, eIndex;

    public MergeThread(int[] pArray, int pStartIndex, int pEndIndex){
        list = pArray;
        sIndex = pStartIndex;
        eIndex = pEndIndex;
    }

    public void run(){
        list = mergeSort(list, sIndex, eIndex);
    }

    /**
     * Merges two sorted int array into one new sorted array.
     * @param lhs
     * @param rhs
     * @return
     */
    private static int[] merge(int[] lhs, int[] rhs) {
        int[] result = new int[lhs.length + rhs.length];

        int leftIndex = 0;
        int rightIndex = 0;
        while(leftIndex < lhs.length && rightIndex < rhs.length) {
            if(lhs[leftIndex] <= rhs[rightIndex]) {
                result[leftIndex + rightIndex] = lhs[leftIndex];
                leftIndex++;
            } else {
                result[leftIndex + rightIndex] = rhs[rightIndex];
                rightIndex++;
            }
        }

        while(leftIndex < lhs.length) {
            result[leftIndex + rightIndex] = lhs[leftIndex];
            leftIndex++;
        }

        while(rightIndex < rhs.length) {
            result[leftIndex + rightIndex] = rhs[rightIndex];
            rightIndex++;
        }

        return result;
    }

    /**
     * Sorts an array from index <code>startIndex</code> (inclusive) to <code>endIndex</code> (exclusive).
     * @param array
     * @param startIndex
     * @param endIndex
     * @return new array that is sorted
     */
    private static int[] mergeSort(int[] array, int startIndex, int endIndex) {
        int length = endIndex - startIndex;
        if(length == 0) {
            return new int[]{};
        }
        if(length == 1) {
            return new int[]{array[startIndex]};
        }

        int halfLength = length / 2;
        //int[] sortedLeftPart = mergeSort(array, startIndex, startIndex + halfLength);
        MergeThread m1 = new MergeThread(array, startIndex, startIndex + halfLength);
        Thread t1 = new Thread(m1);
        t1.start();
        //int[] sortedRightPart = mergeSort(array, startIndex + halfLength, endIndex);
        MergeThread m2 = new MergeThread(array, startIndex + halfLength, endIndex);
        Thread t2 = new Thread(m2);
        t2.start();
        try{
        t1.join();
        t2.join();
        }catch(InterruptedException e){}
        return merge(m1.list, m2.list);     
    }
}

И класс, который фактически запускает процесс

import java.util.Random;

public class Aufg2 {
    public static Random random = new Random(100);

    public static void main(String[] args) {
        int[] array = createRandomArray(10000000);

        long time = System.currentTimeMillis();

        int[] sortedArray = sort(array);

        if(sortedArray.length != array.length || !isSorted(sortedArray)) {
            System.err.println("Failed to sort given array! :-(");
            return;
        }       
        System.out.println("Success! Sorting took " + (System.currentTimeMillis() - time) + "ms.");     
    }

    /**
     * Creates a randomly filled array of given length
     * @param length
     * @return
     */
    private static int[] createRandomArray(int length) {
        int[] result = new int[length];
        for(int i = 0; i < length; i++) {
            result[i] = random.nextInt();
        }
        return result;
    }

    /**
     * Checks whether a given int array is sorted in ascending order  
     * @param array
     * @return <code>true</code> if the given int array is sorted; <code>false</code> otherwise.
     */
    private static boolean isSorted(int[] array) {
        for(int i = 1; i < array.length; i++) {
            if(array[i] < array[i-1]) {
                return false;
            }
        }
        return true;
    }   

    /**
     * Sorts a given array (ascending order)
     * @param array
     * @return
     */
    private static int[] sort(int[] array){
        //TODO: use multiple threads to speed up the sorting
        MergeThread m = new MergeThread(array, 0, array.length);

        try{

        Thread t1 = new Thread(m);
        t1.start();
        t1.join();
        }catch(InterruptedException e){

        }
        return m.list;
    }
}

Однако сортировка слиянием не работает. На консоли печатается много java.lang.OutOfMemmoryError's unable to create new native thread.

Позже сообщение изменится на что-то вроде java heap.

Что мне нужно изменить, чтобы заставить работать сортировку слиянием, и как для этого использовать java.lang.Runtime?

Ответы [ 3 ]

6 голосов
/ 07 мая 2011

Механизм «разделяй и властвуй» заставляет вас пытаться создать что-то вроде 5000000 потоков - и каждый из них хочет по умолчанию 256 КБ (IIRC) стековой памяти.Все еще удивляюсь, почему вы получаете OutOfMemmoryError?

Ограничение числа потоков с помощью пула потоков фиксированного размера - немного поэкспериментируйте с количеством потоков в пуле, но что угоднобольше, чем количество ядер в вашей системе, вряд ли улучшит производительность (и может даже снизить ее).

1 голос
/ 07 мая 2011

Прежде всего используйте ExecutorService и ставьте в нем новые задачи вместо создания миллионов потоков (что должно избавить от первой проблемы; у вас рано или поздно заканчиваются ресурсы, если вы создаете миллионы потоков). Количество ядер в 1,5 раза обычно является хорошим предположением (часто дает лучшие результаты, чем использование доступного количества ядер - но это то, с чем вам придется играть).

И затем - абсолютно важно, если вы хотите, чтобы этот алгоритм работал в любом месте - используйте QuickSort для конечного случая с разумным порогом или InsertionSort, если вы хотите более низкий порог (если вы используете Insertion Sort, размер листа узла 16 или так должно работать нормально).

0 голосов
/ 08 мая 2011

пусть один поток выполняет вторую половину массива, в то время как вызывающий поток обрабатывает первую половину

    int halfLength = length / 2;
    MergeThread m2 = new MergeThread(array, startIndex + halfLength, endIndex);
    Thread t2 = new Thread(m2);
    t2.start();//let new thread handle the second half
    array = mergeSort(array, startIndex, startIndex + halfLength);//do first half ourselves
    try{
    t2.join();
    }catch(InterruptedException e){}
    return merge(array, m2.list);

это уменьшает количество созданных потоков вдвое по сравнению с тем, что у вас было

но быстрая сортировка намного лучше распараллелить, учитывая, что ей не требуется шаг после рекурсии, который позволяет потоку (выполняемое задание с исключителями) возвращаться сразу после делегирования

вызывающей стороне, тогда нужно только следить за тем, когда все заданиясделано

...