Apostolos Fanakis
4 years ago
commit
a099063448
11 changed files with 2827 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||
|
*.out |
||||
|
.idea |
||||
|
*.iml |
@ -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 <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <time.h> |
||||
|
#include <cilk/cilk.h> |
||||
|
#include <cilk/cilk_api.h> |
||||
|
|
||||
|
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<<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){ |
||||
|
#pragma cilk_grainsize = 8192 |
||||
|
cilk_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){ |
||||
|
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; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
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 ); |
||||
|
} |
@ -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 <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <time.h> |
||||
|
#include <cilk/cilk.h> |
||||
|
#include <cilk/cilk_api.h> |
||||
|
|
||||
|
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<<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){ |
||||
|
#pragma cilk_grainsize = 8192 |
||||
|
cilk_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){ |
||||
|
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; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
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 ); |
||||
|
} |
@ -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 <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <time.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <omp.h> |
||||
|
|
||||
|
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<<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){ |
||||
|
#pragma omp parallel for |
||||
|
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){ |
||||
|
#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; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
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 ); |
||||
|
} |
@ -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 <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; |
||||
|
//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<<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; |
||||
|
for (i=lo; i<lo+k; i++){ |
||||
|
compare(i, i+k, dir); |
||||
|
} |
||||
|
|
||||
|
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 lo = thisData->lo; |
||||
|
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; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
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 ); |
||||
|
} |
@ -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 <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <time.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <omp.h> |
||||
|
|
||||
|
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<<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){ |
||||
|
#pragma omp parallel for |
||||
|
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){ |
||||
|
#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; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
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 ); |
||||
|
} |
@ -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 <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; |
||||
|
//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<<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; |
||||
|
for (i=lo; i<lo+k; i++){ |
||||
|
compare(i, i+k, dir); |
||||
|
} |
||||
|
|
||||
|
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 lo = thisData->lo; |
||||
|
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; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
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 ); |
||||
|
} |
@ -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 <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 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<<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); |
||||
|
} |
||||
|
|
||||
|
void *impBitonicSortThread(void * threadArgs){ |
||||
|
impBitonicSortThreadData *thisThreadData = (impBitonicSortThreadData *) threadArgs; |
||||
|
int i; |
||||
|
int N = thisThreadData->N; |
||||
|
int j = thisThreadData->j; |
||||
|
int k = thisThreadData->k; |
||||
|
|
||||
|
for (i; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
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 ); |
||||
|
} |
@ -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 <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 ); |
||||
|
} |
@ -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 <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <time.h> |
||||
|
|
||||
|
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<<atoi(argv[1]); |
||||
|
} |
||||
|
|
||||
|
/** 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"); |
||||
|
} |
||||
|
|
||||
|
/** 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 ); |
||||
|
} |
@ -0,0 +1,215 @@ |
|||||
|
/*
|
||||
|
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> |
||||
|
|
||||
|
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<<atoi(argv[1]); |
||||
|
a = (int *) malloc(N * sizeof(int)); |
||||
|
|
||||
|
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(); |
||||
|
|
||||
|
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("Recursive wall clock time = %f\n", seq_time); |
||||
|
|
||||
|
test(); |
||||
|
|
||||
|
// print();
|
||||
|
} |
||||
|
|
||||
|
/** -------------- SUB-PROCEDURES ----------------- **/ |
||||
|
|
||||
|
/** 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(" TEST %s\n",(pass) ? "PASSed" : "FAILed"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** procedure init() : initialize array "a" with data **/ |
||||
|
void init() { |
||||
|
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; |
||||
|
for (i=lo; i<lo+k; i++) |
||||
|
compare(i, i+k, dir); |
||||
|
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; |
||||
|
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; i<N; i++) { |
||||
|
int ij=i^j; |
||||
|
if ((ij)>i) { |
||||
|
if ((i&k)==0 && a[i] > a[ij]) |
||||
|
exchange(i,ij); |
||||
|
if ((i&k)!=0 && a[i] < a[ij]) |
||||
|
exchange(i,ij); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue