/* 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 typedef enum { false, true } bool; struct timeval startwtime, endwtime; double seq_time; int threadsNum; //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; const int REC_BITONIC_MERGE_PARALLEL_GRAINSIZE = 1 << 14; const int REC_BITONIC_MERGE_PARALLEL_COMPARE_MIN = (1 << 12) - 1; const int REC_BITONIC_MERGE_PARALLEL_CALL_MIN = (1 << 8) - 1; const int REC_BITONIC_SORT_PARALLEL_MIN = (1 << 22) + 1; typedef struct recBitonicSortData{ int threadID; int lo; int cnt; int dir; } recBitonicSortData; pthread_t *threads; int runningThreads = 1; pthread_mutex_t runningThreadsMutex = PTHREAD_MUTEX_INITIALIZER; 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(void *threadArgs); 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); threads = (pthread_t *)malloc(sizeof(pthread_t)*threadsNum); //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 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); } threadsNum = 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){ for (i=lo; i REC_BITONIC_MERGE_PARALLEL_CALL_MIN){ bitonicMerge(lo, k, dir); bitonicMerge(lo+k, k, dir); } 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(void *threadArgs) { recBitonicSortData *thisData = (recBitonicSortData *) threadArgs; int threadID = thisData->threadID; int lo = thisData->lo; int cnt = thisData->cnt; int dir = thisData->dir; if (cnt>1) { if (cnt < REC_BITONIC_SORT_PARALLEL_MIN){ qsort(a+lo, cnt, sizeof(int), (dir == 1 ? qSortAscending : qSortDescending)); } else { pthread_t firstHalf, myOtherHalf; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); int k=cnt/2; pthread_mutex_lock(&runningThreadsMutex); if (runningThreads < threadsNum){ recBitonicSortData newThreadArgs; newThreadArgs.threadID = runningThreads + 1; newThreadArgs.lo = lo; newThreadArgs.cnt = k; newThreadArgs.dir = ASCENDING; if (pthread_create(&firstHalf, &attr, recBitonicSort, (void *) &newThreadArgs)){ printf("Error creating thread. Aborting."); pthread_mutex_unlock(&runningThreadsMutex); exit(1); } else { ++runningThreads; pthread_mutex_unlock(&runningThreadsMutex); } } else { pthread_mutex_unlock(&runningThreadsMutex); qsort(a+lo, k, sizeof(int), qSortAscending); } pthread_mutex_lock(&runningThreadsMutex); if (runningThreads < threadsNum){ recBitonicSortData newThreadArgs; newThreadArgs.threadID = runningThreads + 1; newThreadArgs.lo = lo+k; newThreadArgs.cnt = k; newThreadArgs.dir = DESCENDING; if (pthread_create(&myOtherHalf, &attr, recBitonicSort, (void *) &newThreadArgs)){ printf("Error creating thread. Aborting."); pthread_mutex_unlock(&runningThreadsMutex); exit(1); } else { ++runningThreads; pthread_mutex_unlock(&runningThreadsMutex); } } else { pthread_mutex_unlock(&runningThreadsMutex); qsort(a+lo+k, k, sizeof(int), qSortDescending); } pthread_attr_destroy(&attr); if (&firstHalf != NULL){ pthread_join (firstHalf, NULL); } if (&firstHalf != NULL){ pthread_join (myOtherHalf, NULL); } bitonicMerge(lo, cnt, dir); } } if (threadID > 1 && threadID < threadsNum){ pthread_exit(NULL); } } /** function sort() Caller of recBitonicSort for sorting the entire array of length N in ASCENDING order **/ void sort() { recBitonicSortData newThreadArgs; newThreadArgs.threadID = 1; newThreadArgs.lo = 0; newThreadArgs.cnt = N; newThreadArgs.dir = ASCENDING; recBitonicSort((void *) &newThreadArgs); } /** 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 ); }