You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							320 lines
						
					
					
						
							8.2 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							320 lines
						
					
					
						
							8.2 KiB
						
					
					
				
								/*
							 | 
						|
								 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 <stdio.h>
							 | 
						|
								#include <stdlib.h>
							 | 
						|
								#include <sys/time.h>
							 | 
						|
								#include <time.h>
							 | 
						|
								#include <pthread.h>
							 | 
						|
								
							 | 
						|
								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<<atoi(argv[1]);
							 | 
						|
									N = 1<<atoi(argv[2]);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/** procedure test() : verify sort results **/
							 | 
						|
								void test() {
							 | 
						|
									int pass = 1;
							 | 
						|
									int i;
							 | 
						|
									for (i = 1; i < N; i++) {
							 | 
						|
										pass &= (a[i-1] <= a[i]);
							 | 
						|
									}
							 | 
						|
									printf("\tTEST\t\t%s\n",(pass) ? "PASSed" : "FAILed");
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/** procedure qSortTest() : verify sort results using qsort method **/
							 | 
						|
								void qSortTest(){
							 | 
						|
									sortPass = true;
							 | 
						|
									qsort(a, N, sizeof(int), qSortAscendingCompFuncWithTest);
							 | 
						|
									printf("\tQSORT TEST\t%s\n",(sortPass) ? "PASSed" : "FAILed");
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/** procedure init() : initialize array "a" with data **/
							 | 
						|
								void init() {
							 | 
						|
									srand(randSeed);
							 | 
						|
									int i;
							 | 
						|
									for (i = 0; i < N; i++) {
							 | 
						|
										a[i] = rand() % N; // (N - i);
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/** procedure  print() : print array elements **/
							 | 
						|
								void print() {
							 | 
						|
									int i;
							 | 
						|
									for (i = 0; i < N; i++) {
							 | 
						|
										printf("%d\n", a[i]);
							 | 
						|
									}
							 | 
						|
									printf("\n");
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/** INLINE procedure exchange() : pair swap **/
							 | 
						|
								inline void exchange(int i, int j) {
							 | 
						|
									int t;
							 | 
						|
									t = a[i];
							 | 
						|
									a[i] = a[j];
							 | 
						|
									a[j] = t;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/** procedure compare() 
							 | 
						|
								   The parameter dir indicates the sorting direction, ASCENDING 
							 | 
						|
								   or DESCENDING; if (a[i] > 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<lo+k; i++){
							 | 
						|
												compare(i, i+k, dir);
							 | 
						|
											}
							 | 
						|
										} else {
							 | 
						|
											for (i=lo; i<lo+k; i++){
							 | 
						|
												compare(i, i+k, dir);
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if (cnt > 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 );
							 | 
						|
								}
							 |