import EDU.oswego.cs.dl.util.concurrent.*;
import java.util.Random;
/**
* Sample sort program adapted from a demo in
* Cilk and
* Hood.
*
**/
class MSort {
public static void main (String[] args) {
try {
int n = 262144;
int p = 2;
try {
p = Integer.parseInt(args[0]);
n = Integer.parseInt(args[1]);
}
catch (Exception e) {
System.out.println("Usage: java MSort ");
return;
}
int[] A = new int[n];
// Fill in array A with random values.
Random rng = new Random();
for (int i = 0; i < n; i++) A[i] = rng.nextInt();
int[] workSpace = new int[n];
FJTaskRunnerGroup g = new FJTaskRunnerGroup(p);
Sorter t = new Sorter(A, 0, workSpace, 0, n);
g.invoke(t);
g.stats();
// checkSorted(A, n);
}
catch (InterruptedException ex) {}
}
static void checkSorted (int[] A, int n) {
for (int i = 0; i < n - 1; i++) {
if (A[i] > A[i+1]) {
throw new Error("Unsorted at " + i + ": " + A[i] + " / " + A[i+1]);
}
}
}
/* Threshold values */
// Cutoff for when to do sequential versus parallel merges
static final int MERGE_SIZE = 2048;
// Cutoff for when to do sequential quicksort versus parallel mergesort
static final int QUICK_SIZE = 2048;
// Cutoff for when to use insertion-sort instead of quicksort
static final int INSERTION_SIZE = 20;
static class Sorter extends FJTask {
final int[] A; // Input array.
final int aLo; // offset into the part of array we deal with
final int[] W; // workspace for merge
final int wLo;
final int n; // Number of elements in (sub)arrays.
Sorter (int[] A, int aLo, int[] W, int wLo, int n) {
this.A = A;
this.aLo = aLo;
this.W = W;
this.wLo = wLo;
this.n = n;
}
public void run() {
/*
Algorithm:
IF array size is small, just use a sequential quicksort
Otherwise:
Break array in half.
For each half,
break the half in half (i.e., quarters),
sort the quarters
merge them together
Finally, merge together the two halves.
*/
if (n <= QUICK_SIZE) {
qs();
}
else {
int q = n/4;
coInvoke(new Seq(new Par(new Sorter(A, aLo,
W, wLo,
q),
new Sorter(A, aLo+q,
W, wLo+q,
q)
),
new Merger(A, aLo, q,
A, aLo+q, q,
W, wLo)
),
new Seq(new Par(new Sorter(A, aLo+q*2,
W, wLo+q*2,
q),
new Sorter(A, aLo+q*3,
W, wLo+q*3,
n-q*3)
),
new Merger(A, aLo+q*2, q,
A, aLo+q*3, n-q*3,
W, wLo+q*2)
)
);
invoke(new Merger(W, wLo, q*2,
W, wLo+q*2, n-q*2,
A, aLo));
}
}
/** Relay to quicksort within sync method to ensure memory barriers **/
synchronized void qs() {
quickSort(aLo, aLo+n-1);
}
/** A standard sequential quicksort **/
void quickSort(int lo, int hi) {
// If under threshold, use insertion sort
if (hi-lo+1l <= INSERTION_SIZE) {
for (int i = lo + 1; i <= hi; i++) {
int t = A[i];
int j = i - 1;
while (j >= lo && A[j] > t) {
A[j+1] = A[j];
--j;
}
A[j+1] = t;
}
return;
}
// Use median-of-three(lo, mid, hi) to pick a partition.
// Also swap them into relative order while we are at it.
int mid = (lo + hi) >>> 1;
if (A[lo] > A[mid]) {
int t = A[lo]; A[lo] = A[mid]; A[mid] = t;
}
if (A[mid]> A[hi]) {
int t = A[mid]; A[mid] = A[hi]; A[hi] = t;
if (A[lo]> A[mid]) {
t = A[lo]; A[lo] = A[mid]; A[mid] = t;
}
}
int left = lo+1; // start one past lo since already handled lo
int right = hi-1; // similarly
int partition = A[mid];
for (;;) {
while (A[right] > partition)
--right;
while (left < right && A[left] <= partition)
++left;
if (left < right) {
int t = A[left]; A[left] = A[right]; A[right] = t;
--right;
}
else break;
}
quickSort(lo, left);
quickSort(left+1, hi);
}
}
static class Merger extends FJTask {
final int[] A; // First sorted array.
final int aLo; // first index of A
final int aSize; // number of elements
final int[] B; // Second sorted array.
final int bLo;
final int bSize;
final int[] out; // Output array.
final int outLo;
Merger (int[] A, int aLo, int aSize,
int[] B, int bLo, int bSize,
int[] out, int outLo) {
this.out = out;
this.outLo = outLo;
// A must be largest of the two for split. Might as well swap now.
if (aSize >= bSize) {
this.A = A; this.aLo = aLo; this.aSize = aSize;
this.B = B; this.bLo = bLo; this.bSize = bSize;
}
else {
this.A = B; this.aLo = bLo; this.aSize = bSize;
this.B = A; this.bLo = aLo; this.bSize = aSize;
}
}
public void run() {
/*
Algorithm:
If the arrays are small, then just sequentially merge.
Otherwise:
Split A in half.
Find the greatest point in B less than the beginning
of the second half of A.
In parallel:
merge the left half of A with elements of B up to split point
merge the right half of A with elements of B past split point
*/
if (aSize <= MERGE_SIZE) {
merge();
}
else {
int aHalf = aSize >>> 1;
int bSplit = findSplit(A[aLo + aHalf]);
coInvoke(new Merger(A, aLo, aHalf,
B, bLo, bSplit,
out, outLo),
new Merger(A, aLo+aHalf, aSize-aHalf,
B, bLo+bSplit, bSize-bSplit,
out, outLo+aHalf+bSplit));
}
}
/** find greatest point in B less than value. return 0-based offset **/
synchronized int findSplit(int value) {
int low = 0;
int high = bSize;
while (low < high) {
int middle = low + ((high - low) >>> 1);
if (value <= B[bLo+middle])
high = middle;
else
low = middle + 1;
}
return high;
}
/** A standard sequential merge **/
synchronized void merge() {
int a = aLo;
int aFence = aLo+aSize;
int b = bLo;
int bFence = bLo+bSize;
int k = outLo;
while (a < aFence && b < bFence) {
if (A[a] < B[b])
out[k++] = A[a++];
else
out[k++] = B[b++];
}
while (a < aFence) out[k++] = A[a++];
while (b < bFence) out[k++] = B[b++];
}
}
}