/* bitonic.c This file contains two different implementations of the bitonic sort recursive version : recBitonicSort() imperative version : impBitonicSort() The bitonic sort is also known as Batcher Sort. For a reference of the algorithm, see the article titled Sorting networks and their applications by K. E. Batcher in 1968 The following codes take references to the codes avaiable at http://www.cag.lcs.mit.edu/streamit/results/bitonic/code/c/bitonic.c http://www.tools-of-computing.com/tc/CS/Sorts/bitonic_sort.htm http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm */ /* ------- ---------------------- Nikos Pitsianis, Duke CS ----------------------------- */ #include #include #include #include #include #include typedef enum { false, true } bool; struct timeval startwtime, endwtime; double seq_time; int threads; //number of threads int N; // data array size int *a; // data array to be sorted unsigned randSeed; //seed array initialisation bool sortPass; //flag showing whether the test passed or not const int ASCENDING = 1; const int DESCENDING = 0; //cilk for grainsize const int REC_BITONIC_MERGE_PARALLEL_GRAINSIZE = 1 << 14; //min lenght of an array to be sorted by bitonicMerge in parallel manner const int REC_BITONIC_MERGE_PARALLEL_COMPARE_MIN = (1 << 12) - 1; //min lenght of an array to be merged by bitonicMerge in parallel manner const int REC_BITONIC_MERGE_PARALLEL_CALL_MIN = (1 << 8) - 1; //min lenght of an array to be sorted in parallel manner const int REC_BITONIC_SORT_PARALLEL_MIN = (1 << 22) + 1; void getArgs(int argc, char** argv); void init(void); void print(void); void sort(void); void test(void); void qSortTest(void); void exchange(int i, int j); void compare(int i, int j, int dir); void bitonicMerge(int lo, int cnt, int dir); void recBitonicSort(int lo, int cnt, int dir); void impBitonicSort(void); int qSortAscendingCompFuncWithTest (const void * a, const void * b); int qSortAscending (const void * a, const void * b); int qSortDescending (const void * a, const void * b); /** the main program **/ int main(int argc, char **argv) { getArgs(argc, argv); a = (int *) malloc(N * sizeof(int)); randSeed = (unsigned) time(NULL); if (0!= __cilkrts_set_param("nworkers","0" + threads)){ printf("Failed to set worker count\n"); exit(1); } //Sorts using the qSort algorithm init(); gettimeofday (&startwtime, NULL); qsort(a, N, sizeof(int), qSortAscending); gettimeofday (&endwtime, NULL); seq_time = (double)((endwtime.tv_usec - startwtime.tv_usec)/1.0e6 + endwtime.tv_sec - startwtime.tv_sec); printf("qSort wall clock time = %f\n\n", seq_time); //Sorts using the imperative bitonic algorithm init(); gettimeofday (&startwtime, NULL); impBitonicSort(); gettimeofday (&endwtime, NULL); seq_time = (double)((endwtime.tv_usec - startwtime.tv_usec)/1.0e6 + endwtime.tv_sec - startwtime.tv_sec); printf("Imperative wall clock time = %f\n", seq_time); test(); qSortTest(); //Sorts using the recursive bitonic algorithm init(); gettimeofday (&startwtime, NULL); sort(); gettimeofday (&endwtime, NULL); seq_time = (double)((endwtime.tv_usec - startwtime.tv_usec)/1.0e6 + endwtime.tv_sec - startwtime.tv_sec); printf("\nRecursive wall clock time = %f\n", seq_time); test(); qSortTest(); free(a); } /** -------------- SUB-PROCEDURES ----------------- **/ void getArgs(int argc, char** argv){ if (argc != 3) { printf("Usage: %s p q\nwhere:\n\tP=2^p is the the number of threads(power of two)\n\tN=2^q is problem size (power of two)\n", argv[0]); exit(1); } threads = 1< a[j]) agrees with the direction, then a[i] and a[j] are interchanged. **/ inline void compare(int i, int j, int dir) { if (dir==(a[i]>a[j])) exchange(i,j); } /** Procedure bitonicMerge() It recursively sorts a bitonic sequence in ascending order, if dir = ASCENDING, and in descending order otherwise. The sequence to be sorted starts at index position lo, the parameter cbt is the number of elements to be sorted. **/ void bitonicMerge(int lo, int cnt, int dir) { if (cnt>1) { int k=cnt/2; int i; if (cnt > REC_BITONIC_MERGE_PARALLEL_COMPARE_MIN){ #pragma cilk_grainsize = 8192 cilk_for (i=lo; i REC_BITONIC_MERGE_PARALLEL_CALL_MIN){ cilk_spawn bitonicMerge(lo, k, dir); cilk_spawn bitonicMerge(lo+k, k, dir); cilk_sync; } else{ bitonicMerge(lo, k, dir); bitonicMerge(lo+k, k, dir); } } } /** function recBitonicSort() first produces a bitonic sequence by recursively sorting its two halves in opposite sorting orders, and then calls bitonicMerge to make them in the same order **/ void recBitonicSort(int lo, int cnt, int dir) { if (cnt>1) { if (cnt < REC_BITONIC_SORT_PARALLEL_MIN){ qsort(a+lo, cnt, sizeof(int), (dir == 1 ? qSortAscending : qSortDescending)); return; } int k=cnt/2; cilk_spawn recBitonicSort(lo, k, ASCENDING); cilk_spawn recBitonicSort(lo+k, k, DESCENDING); cilk_sync; bitonicMerge(lo, cnt, dir); } } /** function sort() Caller of recBitonicSort for sorting the entire array of length N in ASCENDING order **/ void sort() { recBitonicSort(0, N, ASCENDING); } /* imperative version of bitonic sort */ void impBitonicSort() { int i,j,k; for (k=2; k<=N; k+=k) { for (j=k>>1; j>0; j=j>>1) { #pragma cilk_grainsize = N/4 cilk_for (i=0; ii) { if ((i&k)==0 && a[i] > a[ij]) exchange(i,ij); if ((i&k)!=0 && a[i] < a[ij]) exchange(i,ij); } } } } } /** function used by qsort for comparing as well as testing **/ int qSortAscendingCompFuncWithTest (const void * a, const void * b) { int result = ( *(int*)a - *(int*)b ); if (result > 0){ sortPass = false; } return result; } /** function used by qsort for comparing **/ int qSortAscending (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); } /** function used by qsort for comparing **/ int qSortDescending (const void * a, const void * b) { return ( *(int*)b - *(int*)a ); }