From a099063448c5b35cfdefc9df54b2d409d76be188 Mon Sep 17 00:00:00 2001 From: apostolof Date: Sun, 25 Apr 2021 12:26:46 +0300 Subject: [PATCH] Squash commit --- .gitignore | 3 + cilk/bitonic.c | 292 +++++++++++++++++++++++++++++ noOpt/bitonicCilk.c | 274 ++++++++++++++++++++++++++++ noOpt/bitonicOpenMP.c | 285 +++++++++++++++++++++++++++++ noOpt/bitonicPthreads.c | 380 ++++++++++++++++++++++++++++++++++++++ openMP/bitonic.c | 304 +++++++++++++++++++++++++++++++ pthreads/bitonic.c | 395 ++++++++++++++++++++++++++++++++++++++++ pthreads/bitonicImp.c | 249 +++++++++++++++++++++++++ pthreads/bitonicRec.c | 320 ++++++++++++++++++++++++++++++++ qsort/qsort.c | 110 +++++++++++ seq/bitonic.c | 215 ++++++++++++++++++++++ 11 files changed, 2827 insertions(+) create mode 100644 .gitignore create mode 100644 cilk/bitonic.c create mode 100644 noOpt/bitonicCilk.c create mode 100644 noOpt/bitonicOpenMP.c create mode 100644 noOpt/bitonicPthreads.c create mode 100644 openMP/bitonic.c create mode 100644 pthreads/bitonic.c create mode 100644 pthreads/bitonicImp.c create mode 100644 pthreads/bitonicRec.c create mode 100644 qsort/qsort.c create mode 100644 seq/bitonic.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b61379f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.out +.idea +*.iml diff --git a/cilk/bitonic.c b/cilk/bitonic.c new file mode 100644 index 0000000..069352e --- /dev/null +++ b/cilk/bitonic.c @@ -0,0 +1,292 @@ +/* + 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 ); +} \ No newline at end of file diff --git a/noOpt/bitonicCilk.c b/noOpt/bitonicCilk.c new file mode 100644 index 0000000..df26b07 --- /dev/null +++ b/noOpt/bitonicCilk.c @@ -0,0 +1,274 @@ +/* + 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 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) { + + 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 ); +} \ No newline at end of file diff --git a/noOpt/bitonicOpenMP.c b/noOpt/bitonicOpenMP.c new file mode 100644 index 0000000..e31ffc9 --- /dev/null +++ b/noOpt/bitonicOpenMP.c @@ -0,0 +1,285 @@ +/* + 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 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; +//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); + + omp_set_dynamic(0); + omp_set_num_threads(threads); + + //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); + } + 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 omp parallel for + for (i=lo; i REC_BITONIC_MERGE_PARALLEL_CALL_MIN){ + #pragma omp parallel sections + { + #pragma omp section + { + bitonicMerge(lo, k, dir); + } + #pragma omp section + { + 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(int lo, int cnt, int dir) { + if (cnt>1) { + int k=cnt/2; + #pragma omp task + { + recBitonicSort(lo, k, ASCENDING); + } + + #pragma omp task + { + recBitonicSort(lo+k, k, DESCENDING); + } + #pragma omp taskwait + bitonicMerge(lo, cnt, dir); + } +} + +/** function sort() + Caller of recBitonicSort for sorting the entire array of length N + in ASCENDING order + **/ +void sort() { + #pragma omp parallel num_threads(threads) + #pragma omp single nowait + recBitonicSort(0, N, ASCENDING); +} + +/* + imperative version of bitonic sort +*/ +void impBitonicSort() { + + int i,j,k; + + for (k=2; k<=N; k=2*k) { + for (j=k>>1; j>0; j=j>>1) { + #pragma omp parallel for num_threads(threads) + 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 ); +} \ No newline at end of file diff --git a/noOpt/bitonicPthreads.c b/noOpt/bitonicPthreads.c new file mode 100644 index 0000000..a3fa1f4 --- /dev/null +++ b/noOpt/bitonicPthreads.c @@ -0,0 +1,380 @@ +/* + 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; +//min lenght of an array to be sorted in parallel manner +const int REC_BITONIC_SORT_PARALLEL_MIN = (1 << 22) + 1; + +//structs for parallel functions +typedef struct recBitonicSortData{ + int threadID; + int lo; + int cnt; + int dir; +} recBitonicSortData; + +typedef struct impBitonicSortThreadData{ + int i; + int N; + int j; + int k; +} impBitonicSortThreadData; + +pthread_t *threads; //array that holds pointers to all threads +int runningThreads = 0; //number of threads currently running +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); +void *impBitonicSortThread(void * threadArgs); +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); + + //allocates space for an array that holds pointers to all threads + 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(threads); + 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; + for (i=lo; ilo; + int cnt = thisData->cnt; + int dir = thisData->dir; + + if (cnt>1) { + if (false){ //small sub-arrays best sorted using qsort + qsort(a+lo, cnt, sizeof(int), (dir == 1 ? qSortAscending : qSortDescending)); + } else { + //plitts problem in two halfs and tries to create a new thread for each + //holds each half's index (from threads array) + int firstHalf = -1, myOtherHalf = -1; + //attribute for joinable threads + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + int k=cnt/2; + pthread_mutex_lock(&runningThreadsMutex); //locks running threads variable + if (runningThreads < threadsNum){ + //arguments for first half + recBitonicSortData newThreadArgs; + newThreadArgs.lo = lo; + newThreadArgs.cnt = k; + newThreadArgs.dir = ASCENDING; + //tries to create a new thread with a pointer to it saved inside array "threads" + if (pthread_create(&threads[runningThreads], &attr, recBitonicSort, (void *) &newThreadArgs)){ + printf("Error creating thread. Aborting."); + pthread_mutex_unlock(&runningThreadsMutex); + exit(1); + } else { //thread was successfully created + //keeps thread's index and updates running threads counter + firstHalf = runningThreads; + ++runningThreads; + pthread_mutex_unlock(&runningThreadsMutex); + } + } else { //max threads number reached, does the job sequentially using qsort + pthread_mutex_unlock(&runningThreadsMutex); + qsort(a+lo, k, sizeof(int), qSortAscending); + } + + pthread_mutex_lock(&runningThreadsMutex); //locks running threads variable + if (runningThreads < threadsNum){ + //arguments for second half + recBitonicSortData newThreadArgs; + newThreadArgs.lo = lo+k; + newThreadArgs.cnt = k; + newThreadArgs.dir = DESCENDING; + //tries to create a new thread with a pointer to it saved inside array "threads" + if (pthread_create(&threads[runningThreads], &attr, recBitonicSort, (void *) &newThreadArgs)){ + printf("Error creating thread. Aborting."); + pthread_mutex_unlock(&runningThreadsMutex); + exit(1); + } else { //thread was successfully created + //keeps thread's index and updates running threads counter + myOtherHalf = runningThreads; + ++runningThreads; + pthread_mutex_unlock(&runningThreadsMutex); + } + } else { //max threads number reached, does the job sequentially using qsort + pthread_mutex_unlock(&runningThreadsMutex); + qsort(a+lo+k, k, sizeof(int), qSortDescending); + } + + pthread_attr_destroy(&attr); + + //achieves synchronization + if (firstHalf != -1){ + pthread_join (threads[firstHalf], NULL); + } + if (myOtherHalf != -1){ + pthread_join (threads[myOtherHalf], NULL); + } + bitonicMerge(lo, cnt, dir); + } + } +} + +/** 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); +} + +void *impBitonicSortThread(void * threadArgs){ + //get variable values from struct + impBitonicSortThreadData *thisThreadData = (impBitonicSortThreadData *) threadArgs; + int i; + int N = thisThreadData->N; + int j = thisThreadData->j; + int k = thisThreadData->k; + + for (i; ii) { + if ((i&k)==0 && a[i] > a[ij]) + exchange(i,ij); + if ((i&k)!=0 && a[i] < a[ij]) + exchange(i,ij); + } + } + pthread_exit(NULL); +} + +/* + imperative version of bitonic sort +*/ +void impBitonicSort() { + int t,j,k; + + //attribute for joinable threads + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + int threadN = N/threadsNum; //splitts work into 2^q sub-problems + + for (k=2; k<=N; k+=k) { + for (j=k>>1; j>0; j=j>>1) { + //creates a new thread for each sub-problem + for (t = 0; t < threadsNum; ++t){ + impBitonicSortThreadData newThreadArgs; + newThreadArgs.i = t * threadN; + newThreadArgs.N = (t + 1) * threadN; + newThreadArgs.j = j; + newThreadArgs.k = k; + int rc = pthread_create(&threads[t], &attr, impBitonicSortThread, (void *) &newThreadArgs); + if (rc){ + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + } + + //achieves synchronization + for(t = 0; t < threadsNum; ++t) { + pthread_join(threads[t], NULL); + } + } + } + pthread_attr_destroy(&attr); +} + +/** 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 ); +} \ No newline at end of file diff --git a/openMP/bitonic.c b/openMP/bitonic.c new file mode 100644 index 0000000..6aa7904 --- /dev/null +++ b/openMP/bitonic.c @@ -0,0 +1,304 @@ +/* + 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 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; +//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); + + omp_set_dynamic(0); + omp_set_num_threads(threads); + + //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 omp parallel for + for (i=lo; i REC_BITONIC_MERGE_PARALLEL_CALL_MIN){ + #pragma omp parallel sections + { + #pragma omp section + { + bitonicMerge(lo, k, dir); + } + #pragma omp section + { + 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(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; + #pragma omp task + { + recBitonicSort(lo, k, ASCENDING); + } + + #pragma omp task + { + recBitonicSort(lo+k, k, DESCENDING); + } + #pragma omp taskwait + bitonicMerge(lo, cnt, dir); + } +} + +/** function sort() + Caller of recBitonicSort for sorting the entire array of length N + in ASCENDING order + **/ +void sort() { + #pragma omp parallel num_threads(threads) + #pragma omp single nowait + recBitonicSort(0, N, ASCENDING); +} + +/* + imperative version of bitonic sort +*/ +void impBitonicSort() { + + int i,j,k; + + for (k=2; k<=N; k=2*k) { + for (j=k>>1; j>0; j=j>>1) { + #pragma omp parallel for num_threads(threads) + 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 ); +} \ No newline at end of file diff --git a/pthreads/bitonic.c b/pthreads/bitonic.c new file mode 100644 index 0000000..706a32e --- /dev/null +++ b/pthreads/bitonic.c @@ -0,0 +1,395 @@ +/* + 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; +//min lenght of an array to be sorted in parallel manner +const int REC_BITONIC_SORT_PARALLEL_MIN = (1 << 22) + 1; + +//structs for parallel functions +typedef struct recBitonicSortData{ + int threadID; + int lo; + int cnt; + int dir; +} recBitonicSortData; + +typedef struct impBitonicSortThreadData{ + int i; + int N; + int j; + int k; +} impBitonicSortThreadData; + +pthread_t *threads; //array that holds pointers to all threads +int runningThreads = 0; //number of threads currently running +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); +void *impBitonicSortThread(void * threadArgs); +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); + + //allocates space for an array that holds pointers to all threads + 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 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(threads); + 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; + for (i=lo; ilo; + int cnt = thisData->cnt; + int dir = thisData->dir; + + if (cnt>1) { + if (cnt < REC_BITONIC_SORT_PARALLEL_MIN){ //small sub-arrays best sorted using qsort + qsort(a+lo, cnt, sizeof(int), (dir == 1 ? qSortAscending : qSortDescending)); + } else { + //plitts problem in two halfs and tries to create a new thread for each + //holds each half's index (from threads array) + int firstHalf = -1, myOtherHalf = -1; + //attribute for joinable threads + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + int k=cnt/2; + pthread_mutex_lock(&runningThreadsMutex); //locks running threads variable + if (runningThreads < threadsNum){ + //arguments for first half + recBitonicSortData newThreadArgs; + newThreadArgs.lo = lo; + newThreadArgs.cnt = k; + newThreadArgs.dir = ASCENDING; + //tries to create a new thread with a pointer to it saved inside array "threads" + if (pthread_create(&threads[runningThreads], &attr, recBitonicSort, (void *) &newThreadArgs)){ + printf("Error creating thread. Aborting."); + pthread_mutex_unlock(&runningThreadsMutex); + exit(1); + } else { //thread was successfully created + //keeps thread's index and updates running threads counter + firstHalf = runningThreads; + ++runningThreads; + pthread_mutex_unlock(&runningThreadsMutex); + } + } else { //max threads number reached, does the job sequentially using qsort + pthread_mutex_unlock(&runningThreadsMutex); + qsort(a+lo, k, sizeof(int), qSortAscending); + } + + pthread_mutex_lock(&runningThreadsMutex); //locks running threads variable + if (runningThreads < threadsNum){ + //arguments for second half + recBitonicSortData newThreadArgs; + newThreadArgs.lo = lo+k; + newThreadArgs.cnt = k; + newThreadArgs.dir = DESCENDING; + //tries to create a new thread with a pointer to it saved inside array "threads" + if (pthread_create(&threads[runningThreads], &attr, recBitonicSort, (void *) &newThreadArgs)){ + printf("Error creating thread. Aborting."); + pthread_mutex_unlock(&runningThreadsMutex); + exit(1); + } else { //thread was successfully created + //keeps thread's index and updates running threads counter + myOtherHalf = runningThreads; + ++runningThreads; + pthread_mutex_unlock(&runningThreadsMutex); + } + } else { //max threads number reached, does the job sequentially using qsort + pthread_mutex_unlock(&runningThreadsMutex); + qsort(a+lo+k, k, sizeof(int), qSortDescending); + } + + pthread_attr_destroy(&attr); + + //achieves synchronization + if (firstHalf != -1){ + pthread_join (threads[firstHalf], NULL); + } + if (myOtherHalf != -1){ + pthread_join (threads[myOtherHalf], NULL); + } + bitonicMerge(lo, cnt, dir); + } + } +} + +/** 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); +} + +void *impBitonicSortThread(void * threadArgs){ + //get variable values from struct + impBitonicSortThreadData *thisThreadData = (impBitonicSortThreadData *) threadArgs; + int i; + int N = thisThreadData->N; + int j = thisThreadData->j; + int k = thisThreadData->k; + + for (i; ii) { + if ((i&k)==0 && a[i] > a[ij]) + exchange(i,ij); + if ((i&k)!=0 && a[i] < a[ij]) + exchange(i,ij); + } + } + pthread_exit(NULL); +} + +/* + imperative version of bitonic sort +*/ +void impBitonicSort() { + int t,j,k; + + //attribute for joinable threads + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + int threadN = N/threadsNum; //splitts work into 2^q sub-problems + + for (k=2; k<=N; k+=k) { + for (j=k>>1; j>0; j=j>>1) { + //creates a new thread for each sub-problem + for (t = 0; t < threadsNum; ++t){ + impBitonicSortThreadData newThreadArgs; + newThreadArgs.i = t * threadN; + newThreadArgs.N = (t + 1) * threadN; + newThreadArgs.j = j; + newThreadArgs.k = k; + int rc = pthread_create(&threads[t], &attr, impBitonicSortThread, (void *) &newThreadArgs); + if (rc){ + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + } + + //achieves synchronization + for(t = 0; t < threadsNum; ++t) { + pthread_join(threads[t], NULL); + } + } + } + pthread_attr_destroy(&attr); +} + +/** 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 ); +} \ No newline at end of file diff --git a/pthreads/bitonicImp.c b/pthreads/bitonicImp.c new file mode 100644 index 0000000..6c678b9 --- /dev/null +++ b/pthreads/bitonicImp.c @@ -0,0 +1,249 @@ +/* + 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 impBitonicSortThreadData{ + int i; + int N; + int j; + int k; +} impBitonicSortThreadData; + +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 *impBitonicSortThread(void * threadArgs); +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); + + //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(); + + 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); +} + +void *impBitonicSortThread(void * threadArgs){ + impBitonicSortThreadData *thisThreadData = (impBitonicSortThreadData *) threadArgs; + int i; + int N = thisThreadData->N; + int j = thisThreadData->j; + int k = thisThreadData->k; + + for (i; ii) { + if ((i&k)==0 && a[i] > a[ij]) + exchange(i,ij); + if ((i&k)!=0 && a[i] < a[ij]) + exchange(i,ij); + } + } + pthread_exit(NULL); +} + +/* + imperative version of bitonic sort +*/ +void impBitonicSort() { + int t,j,k; + + impBitonicSortThreadData impBitonicSortThreadDataArray[threadsNum]; + pthread_t threads[threadsNum]; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + int threadN = N/threadsNum; + + for (k=2; k<=N; k+=k) { + for (j=k>>1; j>0; j=j>>1) { + for (t = 0; t < threadsNum; ++t){ + impBitonicSortThreadDataArray[t].i = t * threadN; + impBitonicSortThreadDataArray[t].N = (t + 1) * threadN; + impBitonicSortThreadDataArray[t].j = j; + impBitonicSortThreadDataArray[t].k = k; + int rc = pthread_create(&threads[t], &attr, impBitonicSortThread, (void *) &impBitonicSortThreadDataArray[t]); + if (rc){ + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + } + void *status; + for(t = 0; t < threadsNum; ++t) { + int rc = pthread_join(threads[t], &status); + if (rc) { + printf("ERROR; return code from pthread_join() is %d\n", rc); + exit(-1); + } + } + } + } + pthread_attr_destroy(&attr); +} + +/** 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 ); +} \ No newline at end of file diff --git a/pthreads/bitonicRec.c b/pthreads/bitonicRec.c new file mode 100644 index 0000000..5353dc8 --- /dev/null +++ b/pthreads/bitonicRec.c @@ -0,0 +1,320 @@ +/* + 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 ); +} \ No newline at end of file diff --git a/qsort/qsort.c b/qsort/qsort.c new file mode 100644 index 0000000..6bb0e0d --- /dev/null +++ b/qsort/qsort.c @@ -0,0 +1,110 @@ +/* + 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 + +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 + +const int ASCENDING = 1; +const int DESCENDING = 0; + +void getArgs(int argc, char** argv); +void init(void); +void print(void); +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); + + //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("%f\n", seq_time); + + free(a); +} + +/** -------------- SUB-PROCEDURES ----------------- **/ + +void getArgs(int argc, char** argv){ + if (argc != 2) { + 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); + } + N = 1< +#include +#include + +struct timeval startwtime, endwtime; +double seq_time; + + +int N; // data array size +int *a; // data array to be sorted + +const int ASCENDING = 1; +const int DESCENDING = 0; + + +void init(void); +void print(void); +void sort(void); +void test(void); +inline 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); + + +/** the main program **/ +int main(int argc, char **argv) { + + if (argc != 2) { + printf("Usage: %s q\n where n=2^q is problem size (power of two)\n", + argv[0]); + exit(1); + } + + N = 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; + for (i=lo; i1) { + int k=cnt/2; + recBitonicSort(lo, k, ASCENDING); + recBitonicSort(lo+k, k, DESCENDING); + 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=2*k) { + for (j=k>>1; j>0; j=j>>1) { + 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); + } + } + } + } +}