/*
* File: mpi_odd_even.c
* Purpose: Implement parallel odd-even sort of an array of
* nonegative ints
* Input:
* A: elements of array (optional)
* Output:
* A: elements of A after sorting
*
* Compile: mpicc -g -Wall -o mpi_odd_even mpi_odd_even.c
* Run:
* mpiexec -n
mpi_odd_even
* - p: the number of processes
* - g: generate random, distributed list
* - i: user will input list on process 0
* - global_n: number of elements in global list
*
* Notes:
* 1. global_n must be evenly divisible by p
* 2. Except for debug output, process 0 does all I/O
* 3. Optional -DDEBUG compile flag for verbose output
*/
#include
#include
#include
#include
const int RMAX = 100;
/* Local functions */
void Usage(char* program);
void Print_list(int local_A[], int local_n, int rank);
void Merge_low(int local_A[], int temp_B[], int temp_C[],
int local_n);
void Merge_high(int local_A[], int temp_B[], int temp_C[],
int local_n);
void Generate_list(int local_A[], int local_n, int my_rank);
int Compare(const void* a_p, const void* b_p);
/* Functions involving communication */
void Get_args(int argc, char* argv[], int* global_n_p, int* local_n_p,
char* gi_p, int my_rank, int p, MPI_Comm comm);
void Sort(int local_A[], int local_n, int my_rank,
int p, MPI_Comm comm);
void Odd_even_iter(int local_A[], int temp_B[], int temp_C[],
int local_n, int phase, int even_partner, int odd_partner,
int my_rank, int p, MPI_Comm comm);
void Print_local_lists(int local_A[], int local_n,
int my_rank, int p, MPI_Comm comm);
void Print_global_list(int local_A[], int local_n, int my_rank,
int p, MPI_Comm comm);
void Read_list(int local_A[], int local_n, int my_rank, int p,
MPI_Comm comm);
/*-------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
int my_rank, p;
char g_i;
int *local_A;
int global_n;
int local_n;
MPI_Comm comm;
MPI_Init(&argc, &argv);
comm = MPI_COMM_WORLD;
MPI_Comm_size(comm, &p);
MPI_Comm_rank(comm, &my_rank);
Get_args(argc, argv, &global_n, &local_n, &g_i, my_rank, p, comm);
local_A = (int*) malloc(local_n*sizeof(int));
if (g_i == 'g') {
Generate_list(local_A, local_n, my_rank);
Print_local_lists(local_A, local_n, my_rank, p, comm);
} else {
Read_list(local_A, local_n, my_rank, p, comm);
# ifdef DEBUG
Print_local_lists(local_A, local_n, my_rank, p, comm);
# endif
}
# ifdef DEBUG
printf("Proc %d > Before Sort\n", my_rank);
fflush(stdout);
# endif
Sort(local_A, local_n, my_rank, p, comm);
# ifdef DEBUG
Print_local_lists(local_A, local_n, my_rank, p, comm);
fflush(stdout);
# endif
Print_global_list(local_A, local_n, my_rank, p, comm);
free(local_A);
MPI_Finalize();
return 0;
} /* main */
/*-------------------------------------------------------------------
* Function: Generate_list
* Purpose: Fill list with random ints
* Input Args: local_n, my_rank
* Output Arg: local_A
*/
void Generate_list(int local_A[], int local_n, int my_rank) {
int i;
srandom(my_rank+1);
for (i = 0; i < local_n; i++)
local_A[i] = random() % RMAX;
} /* Generate_list */
/*-------------------------------------------------------------------
* Function: Usage
* Purpose: Print command line to start program
* In arg: program: name of executable
* Note: Purely local, run only by process 0;
*/
void Usage(char* program) {
fprintf(stderr, "usage: mpirun -np %s \n",
program);
fprintf(stderr, " - p: the number of processes \n");
fprintf(stderr, " - g: generate random, distributed list\n");
fprintf(stderr, " - i: user will input list on process 0\n");
fprintf(stderr, " - global_n: number of elements in global list");
fprintf(stderr, " (must be evenly divisible by p)\n");
fflush(stderr);
} /* Usage */
/*-------------------------------------------------------------------
* Function: Get_args
* Purpose: Get and check command line arguments
* Input args: argc, argv, my_rank, p, comm
* Output args: global_n_p, local_n_p, gi_p
*/
void Get_args(int argc, char* argv[], int* global_n_p, int* local_n_p,
char* gi_p, int my_rank, int p, MPI_Comm comm) {
if (my_rank == 0) {
if (argc != 3) {
Usage(argv[0]);
*global_n_p = -1; /* Bad args, quit */
} else {
*gi_p = argv[1][0];
if (*gi_p != 'g' && *gi_p != 'i') {
Usage(argv[0]);
*global_n_p = -1; /* Bad args, quit */
} else {
*global_n_p = atoi(argv[2]);
if (*global_n_p % p != 0) {
Usage(argv[0]);
*global_n_p = -1;
}
}
}
} /* my_rank == 0 */
MPI_Bcast(gi_p, 1, MPI_CHAR, 0, comm);
MPI_Bcast(global_n_p, 1, MPI_INT, 0, comm);
if (*global_n_p <= 0) {
MPI_Finalize();
exit(-1);
}
*local_n_p = *global_n_p/p;
# ifdef DEBUG
printf("Proc %d > gi = %c, global_n = %d, local_n = %d\n",
my_rank, *gi_p, *global_n_p, *local_n_p);
fflush(stdout);
# endif
} /* Get_args */
/*-------------------------------------------------------------------
* Function: Read_list
* Purpose: process 0 reads the list from stdin and scatters it
* to the other processes.
* In args: local_n, my_rank, p, comm
* Out arg: local_A
*/
void Read_list(int local_A[], int local_n, int my_rank, int p,
MPI_Comm comm) {
int i;
int *temp;
if (my_rank == 0) {
temp = (int*) malloc(p*local_n*sizeof(int));
printf("Enter the elements of the list\n");
for (i = 0; i < p*local_n; i++)
scanf("%d", &temp[i]);
}
MPI_Scatter(temp, local_n, MPI_INT, local_A, local_n, MPI_INT,
0, comm);
if (my_rank == 0)
free(temp);
} /* Read_list */
/*-------------------------------------------------------------------
* Function: Print_global_list
* Purpose: Print the contents of the global list A
* Input args:
* n, the number of elements
* A, the list
* Note: Purely local, called only by process 0
*/
void Print_global_list(int local_A[], int local_n, int my_rank, int p,
MPI_Comm comm) {
int* A;
int i, n;
if (my_rank == 0) {
n = p*local_n;
A = (int*) malloc(n*sizeof(int));
MPI_Gather(local_A, local_n, MPI_INT, A, local_n, MPI_INT, 0,
comm);
printf("Global list:\n");
for (i = 0; i < n; i++)
printf("%d ", A[i]);
printf("\n\n");
free(A);
} else {
MPI_Gather(local_A, local_n, MPI_INT, A, local_n, MPI_INT, 0,
comm);
}
} /* Print_global_list */
/*-------------------------------------------------------------------
* Function: Compare
* Purpose: Compare 2 ints, return -1, 0, or 1, respectively, when
* the first int is less than, equal, or greater than
* the second. Used by qsort.
*/
int Compare(const void* a_p, const void* b_p) {
int a = *((int*)a_p);
int b = *((int*)b_p);
if (a < b)
return -1;
else if (a == b)
return 0;
else /* a > b */
return 1;
} /* Compare */
/*-------------------------------------------------------------------
* Function: Sort
* Purpose: Sort local list, use odd-even sort to sort
* global list.
* Input args: local_n, my_rank, p, comm
* In/out args: local_A
*/
void Sort(int local_A[], int local_n, int my_rank,
int p, MPI_Comm comm) {
int phase;
int *temp_B, *temp_C;
int even_partner; /* phase is even or left-looking */
int odd_partner; /* phase is odd or right-looking */
/* Temporary storage used in merge-split */
temp_B = (int*) malloc(local_n*sizeof(int));
temp_C = (int*) malloc(local_n*sizeof(int));
/* Find partners: negative rank => do nothing during phase */
if (my_rank % 2 != 0) {
even_partner = my_rank - 1;
odd_partner = my_rank + 1;
if (odd_partner == p) odd_partner = MPI_PROC_NULL; // Idle during odd phase
} else {
even_partner = my_rank + 1;
if (even_partner == p) even_partner = MPI_PROC_NULL; // Idle during even phase
odd_partner = my_rank-1;
}
/* Sort local list using built-in quick sort */
qsort(local_A, local_n, sizeof(int), Compare);
# ifdef DEBUG
printf("Proc %d > before loop in sort\n", my_rank);
fflush(stdout);
# endif
for (phase = 0; phase < p; phase++)
Odd_even_iter(local_A, temp_B, temp_C, local_n, phase,
even_partner, odd_partner, my_rank, p, comm);
free(temp_B);
free(temp_C);
} /* Sort */
/*-------------------------------------------------------------------
* Function: Odd_even_iter
* Purpose: One iteration of Odd-even transposition sort
* In args: local_n, phase, my_rank, p, comm
* In/out args: local_A
* Scratch: temp_B, temp_C
*/
void Odd_even_iter(int local_A[], int temp_B[], int temp_C[],
int local_n, int phase, int even_partner, int odd_partner,
int my_rank, int p, MPI_Comm comm) {
MPI_Status status;
if (phase % 2 == 0) {
if (even_partner >= 0) {
MPI_Sendrecv(local_A, local_n, MPI_INT, even_partner, 0,
temp_B, local_n, MPI_INT, even_partner, 0, comm,
&status);
if (my_rank % 2 != 0)
Merge_high(local_A, temp_B, temp_C, local_n);
else
Merge_low(local_A, temp_B, temp_C, local_n);
}
} else { /* odd phase */
if (odd_partner >= 0) {
MPI_Sendrecv(local_A, local_n, MPI_INT, odd_partner, 0,
temp_B, local_n, MPI_INT, odd_partner, 0, comm,
&status);
if (my_rank % 2 != 0)
Merge_low(local_A, temp_B, temp_C, local_n);
else
Merge_high(local_A, temp_B, temp_C, local_n);
}
}
} /* Odd_even_iter */
/*-------------------------------------------------------------------
* Function: Merge_low
* Purpose: Merge the smallest local_n elements in my_keys
* and recv_keys into temp_keys. Then copy temp_keys
* back into my_keys.
* In args: local_n, recv_keys
* In/out args: my_keys
* Scratch: temp_keys
*/
void Merge_low(
int my_keys[], /* in/out */
int recv_keys[], /* in */
int temp_keys[], /* scratch */
int local_n /* = n/p, in */) {
int m_i, r_i, t_i;
m_i = r_i = t_i = 0;
while (t_i < local_n) {
if (my_keys[m_i] <= recv_keys[r_i]) {
temp_keys[t_i] = my_keys[m_i];
t_i++; m_i++;
} else {
temp_keys[t_i] = recv_keys[r_i];
t_i++; r_i++;
}
}
memcpy(my_keys, temp_keys, local_n*sizeof(int));
} /* Merge_low */
/*-------------------------------------------------------------------
* Function: Merge_high
* Purpose: Merge the largest local_n elements in local_A
* and temp_B into temp_C. Then copy temp_C
* back into local_A.
* In args: local_n, temp_B
* In/out args: local_A
* Scratch: temp_C
*/
void Merge_high(int local_A[], int temp_B[], int temp_C[],
int local_n) {
int ai, bi, ci;
ai = local_n-1;
bi = local_n-1;
ci = local_n-1;
while (ci >= 0) {
if (local_A[ai] >= temp_B[bi]) {
temp_C[ci] = local_A[ai];
ci--; ai--;
} else {
temp_C[ci] = temp_B[bi];
ci--; bi--;
}
}
memcpy(local_A, temp_C, local_n*sizeof(int));
} /* Merge_high */
/*-------------------------------------------------------------------
* Only called by process 0
*/
void Print_list(int local_A[], int local_n, int rank) {
int i;
printf("%d: ", rank);
for (i = 0; i < local_n; i++)
printf("%d ", local_A[i]);
printf("\n");
} /* Print_list */
/*-------------------------------------------------------------------
* Function: Print_local_lists
* Purpose: Print each process' current list contents
* Input args: all
* Notes:
* 1. Assumes all participating processes are contributing local_n
* elements
*/
void Print_local_lists(int local_A[], int local_n,
int my_rank, int p, MPI_Comm comm) {
int* A;
int q;
MPI_Status status;
if (my_rank == 0) {
A = (int*) malloc(local_n*sizeof(int));
Print_list(local_A, local_n, my_rank);
for (q = 1; q < p; q++) {
MPI_Recv(A, local_n, MPI_INT, q, 0, comm, &status);
Print_list(A, local_n, q);
}
free(A);
} else {
MPI_Send(local_A, local_n, MPI_INT, 0, 0, comm);
}
} /* Print_local_lists */