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 );
|
|
}
|