// MergeSort.java // Merge Sort Parallel Test... // Bryan Chadwick : Sep. 2008 // All the needed functionality to run simple parallel tests on // multiple processors. // Real (pretty good) results with as little as 20,000 elements: // java -Xss2M MergeSort 20000 // Where -Xss2M tells Java to use 2Mb thread stacks (functional lists // are tough without proper tail recursion...) public class MergeSort{ static void p(String s){ System.out.println(s); } // Main Method public static void main(String[] s){ int len = s.length>0?Integer.parseInt(s[0]):2000; L l = rand(new id(), len); p(" Length = "+len); // Sequential, Parallel, and Array based Sorts... p(" Sort: "+new Sorter().timesort(l)); p(" PSort: "+new PSorter().timesort(l)); p(" ASort: "+new ASorter().timesort(l, new Integer[len])); } // Create a D from an integer... so we can build random lists // of different kinds of things for testing static abstract class Creator{ abstract D make(int i); } // ID for Integers static class id extends Creator{ Integer make(int i){ return i; } } // Make a List with a Creator and integer parameters static > L make(D ... xs){ return make(xs,0,xs.length); } static > L make(D[] xs, int i, int len){ return (i==len)?new L():make(xs,i+1,len).push(xs[i]); } // Make a List with a Creator and integer parameters static > L make(Creator c, int ... xs){ return make(c,xs,xs.length-1); } // Same here... but from an array, where 'i' is the index currently // being worked on (count down) static > L make(Creator c, int[] xs, int i){ return (i < 0)?new L():make(c,xs,i-1).push(c.make(xs[i])); } // Return a Random integer in [0..19] static int randInt(){ return (int)(Math.random()*20); } // Make a List with a Creator and random integers static > L rand(Creator c, int k){ return (k==0?new L():rand(c,k-1).push(c.make(randInt()))); } // Dynamic Sorter Object static class Sorter>{ // Outer Sort Call public L sort(L l){ return recsort(l); } // Inner Sort Call for recursion public L recsort(L l){ int len = l.length(); if(len <= 1)return l; L.P p = l.split(len/2); //p(" recsort["+Thread.currentThread().getId()+"]: "+l); return merge(recsort(p.lt), recsort(p.rt)); } // Timing Result class class T{ long mil; L lst; T(long m, L l){ mil = m; lst = l; } public String toString(){ return " MSec: "+mil; }//+" : "+lst; } } // Caculate Timing Results for List sorting public T timesort(L l){ // Make sure Java doesn't interrupt us... System.gc();Thread.yield(); long tm = System.currentTimeMillis(); L r = sort(l); // Must be careful about argument evaluation order... return new T(System.currentTimeMillis()-tm, r); } } // Sort Using java.util.Arrays static class ASorter> extends Sorter{ public T timesort(L l, D[] a){ a = l.toArray(a); System.gc();Thread.yield(); long tm = System.currentTimeMillis(); java.util.Arrays.sort(a); return new T(System.currentTimeMillis()-tm, make(a)); } } // Parallel Sorter version static class PSorter> extends Sorter{ // Outer Sort call splits into a seperate thread for the // Left Split (half) of the list. public L sort(L l){ int len = l.length(); if(len <= 1)return l; L.P p = l.split(len/2); Thrd lt = new Thrd(p.lt); lt.start(); L rt = recsort(p.rt); p = null; lt.waitFor(); return merge(lt.res, rt); } // Thunkish class to support delayed sorting in a separate thread class Thrd extends Thread{ L lst, res; boolean fin = false; Thrd(L l){ lst = l; res = null; } public void run(){ res = recsort(lst); done(); } synchronized void done(){ fin = true; this.notifyAll(); } synchronized void waitFor(){ try{ while(!fin)wait(); }catch(InterruptedException e){} } } } // Classic merge method static > L merge(L l1, L l2){ if(l1.isEmpty())return l2; if(l2.isEmpty())return l1; if(l1.first().compareTo(l2.first()) <= 0) return merge(l1.rest(), l2).push(l1.first()); return merge(l1, l2.rest()).push(l2.first()); } // Functional Generic Lists with the typical methods static class L>{ // Inner class for Pair of Lists when Splitting static class P>{ L lt, rt; P(L l, L r){ lt = l; rt = r;} public String toString(){ return "<"+lt+" :: "+rt+">"; } } boolean isEmpty(){ return true; } D first(){ throw new RuntimeException("L.first() Bad"); } L rest(){ throw new RuntimeException("L.rest() Bad"); } int length(){ return 0; } L reverse(){ return reverse(new L()); } L reverse(L ac){ return ac; } L push(D d){ return new C(d, this); } L append(L l){ return l; } P split(int k){ return split(k,new L()); } P split(int k, L ac){ throw new RuntimeException("L.split() Bad"); } public String toString(){ return ""; } D[] toArray(D[] d){ return toArray(d, 0); } D[] toArray(D[] d, int i){ return d; } } static class C> extends L{ D f; L r; C(D ff, L rr){ f = ff; r = rr; } boolean isEmpty(){ return false; } D first(){ return f; } L rest(){ return r; } int length(){ return 1+r.length(); } L reverse(L ac){ return r.reverse(ac.push(f)); } L append(L l){ return r.append(l).push(f); } P split(int k, L ac){ return k==0?new P(ac.reverse(),this):r.split(k-1, ac.push(f)); } public String toString(){ return f+" "+r; } D[] toArray(D[] d, int i){ d[i] = f; return r.toArray(d, i+1); } } }