/* * Java program to test/time quicksort. * * parallel version, somewhat more sophisticated approach: * * sets up a pool of threads, and on split creates independent task (for * the thread pool) for every subproblem with at least the designated * number of elements per task. * * command-line arguments: number of elements to sort, seed for RNG, number * of threads, elements per task. */ package csci3366.hw4; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class QuickSortPar2 { /* global variables for things to be shared by tasks/threads */ public static ExecutorService threadPool; public static int minTaskElements; /* main method */ public static void main(String[] args) { String usageMsg = "arguments are numElements seed numThreads pointsPerTask"; /* process command-line arguments */ if (args.length < 4) { System.err.println(usageMsg); System.exit(1); } int n = 0; int seed = 0; int numThreads = 0; try { n = Integer.parseInt(args[0]); seed = Integer.parseInt(args[1]); numThreads = Integer.parseInt(args[2]); minTaskElements = Integer.parseInt(args[3]); } catch (NumberFormatException e) { System.err.println("arguments must be integers"); System.exit(1); } /* generate data */ Integer[] ints = new Integer[n]; Random randGen = new Random((long) seed); for (int i = 0; i < ints.length; ++i) { ints[i] = new Integer(randGen.nextInt()); } /* sort (time this part only) */ long startTime = System.currentTimeMillis(); threadPool = Executors.newFixedThreadPool(numThreads); sort(ints); threadPool.shutdown(); try { threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { System.err.println("should not happen"); } long endTime = System.currentTimeMillis(); /* check whether sort succeeded */ if (isSorted(ints)) { System.out.println("sort succeeded"); } else { System.out.println("sort failed"); } /* print timing result */ System.out.println("Parallel version, v2, with " + numThreads + " threads, " + minTaskElements + " elements per task"); System.out.println("Time for sort of " + n + " random integers (seed " + seed + ") is " + ((double) (endTime - startTime) / 1000) + " seconds"); } /* check whether array is sorted */ public static boolean isSorted(Integer[] data) { for (int i = 0; i < data.length-1; ++i) { if (data[i].compareTo(data[i+1]) > 0) return false; } return true; } /* main quicksort routine */ public static void sort(Integer[] data) { TaskCode.numTasksRemaining = 0; TaskCode.numTasksTotal = 0; sort(data, 0, data.length-1); /* wait for all tasks to complete */ TaskCode.waitUntilAllComplete(); /* FIXME? System.err.println(TaskCode.numTasksTotal + " tasks created"); */ } /* * recursive quicksort: * sorts data[firstIndex..lastIndex] in place, * using threadPool and at most minTaskElements per task */ private static void sort(Integer[] data, int startIndex, int endIndex) { if (startIndex < endIndex) { int splitIndex = partition(data, startIndex, endIndex); /* create new tasks for either or both subproblems */ boolean leftTask = newTask(data, startIndex, splitIndex-1); boolean rightTask = newTask(data, splitIndex+1, endIndex); /* solve subproblems for which no new task */ if (!leftTask) { sort(data, startIndex, splitIndex-1); } if (!rightTask) { sort(data, splitIndex+1, endIndex); } } } /* * rearranges array elements from startIndex through endIndex such that * elements <= pivot are to the left of splitIndex and elements > pivot * are to the right of splitIndex, places pivot at splitIndex, and * returns splitIndex. */ private static int partition(Integer[] data, int startIndex, int endIndex) { int k = startIndex + 1; int m = startIndex + 1; /* * loop invariant: * data[startIndex .. k-1] <= data[startIndex] * data[k .. m-1] > data[startIndex] */ while (m <= endIndex) { if (data[m].compareTo(data[startIndex]) > 0) { } else { swap(data, k, m); ++k; } ++m; } swap(data, startIndex, k-1); return k-1; } /* swaps array elements */ private static void swap(Integer[] data, int i, int j) { Integer temp = data[i]; data[i] = data[j]; data[j] = temp; } /* * decide whether to sort this half in a new task * if enough elements, start new task and return true * else return false */ private static boolean newTask(Integer[] data, int startIndex, int endIndex) { if ((endIndex - startIndex) >= minTaskElements) { threadPool.execute(new TaskCode(data, startIndex, endIndex)); /* FIXME? System.err.printf("new tasks for %d elements\n", endIndex - startIndex); */ return true; } else { return false; } } /* static inner class to contain code to run as task */ private static class TaskCode implements Runnable { private Integer[] data; private int firstIndex; private int lastIndex; public static int numTasksRemaining; public static int numTasksTotal; private static Object lock = new Object(); public TaskCode(Integer[] d, int first, int last) { data = d; firstIndex = first; lastIndex = last; synchronized (lock) { ++numTasksRemaining; ++numTasksTotal; } } public void run() { sort(data, firstIndex, lastIndex); synchronized (lock) { --numTasksRemaining; lock.notifyAll(); } } public static void waitUntilAllComplete() { synchronized (lock) { while (numTasksRemaining > 0) { try { lock.wait(); } catch (InterruptedException e) { System.err.println("should not happen"); } } } } } }