/*
* File: prog3.8_mpi_mergesort.c
* Purpose: Implement merge sort of an array of nonnegative ints
*
* Compile:
* mpicc -g -Wall -o prog3.8_mpi_mergesort prog3.8_mpi_mergesort.c
*
* Usage:
* mpiexec -n
prog3.8_mpi_mergesort
* - 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
*
* Input:
* A: elements of array (optional)
* Output:
* A: elements of A after sorting
*
* Notes:
* 1. global_n must be evenly divisible by p
* 2. Except for debug output, process 0 does all I/O
* 3. p should be a power of 2
* 4. Optional -DDEBUG compile flag for verbose output
*
* IPP: Programming Assignment 3.8
*/
#include
#include
#include
#define RMAX 100
/* Local functions */
void Usage(char* program);
void Init_list(int local_A[], int n);
void Print_global_list(int global_A[], int global_n);
void Print_list(int local_A[], int local_n, int rank);
void Merge(int local_A[], int local_B[], int* local_n_p);
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 global_n, int* local_n_p, int my_rank,
int p, MPI_Comm comm);
void Print_local_lists(int local_A[], int local_n, unsigned bitmask,
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(global_n*sizeof(int));
Init_list(local_A, global_n);
if (g_i == 'g') {
Generate_list(local_A, local_n, my_rank);
Print_local_lists(local_A, local_n, 0, my_rank, p, comm);
} else {
Read_list(local_A, local_n, my_rank, p, comm);
# ifdef DEBUG
Print_local_lists(local_A, local_n, 0, my_rank, p, comm);
# endif
}
Sort(local_A, global_n, &local_n, my_rank, p, comm);
if (my_rank == 0)
Print_global_list(local_A, global_n);
free(local_A);
MPI_Finalize();
return 0;
} /* main */
/*-------------------------------------------------------------------
* Function: Init_list
* Purpose: Initialize list A to list of -1's
* Input arg: n
* Output arg: A
* Note: Purely local, run by each process
*/
void Init_list(int A[], int n) {
int i;
for (i = 0; i < n; i++)
A[i] = -1;
} /* Init_list */
/*-------------------------------------------------------------------
* 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: mpiexec -n %s \n",
program);
fprintf(stderr, " - p: the number of processes (a power of 2)\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]);
}
}
} /* 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) || (*global_n_p % 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 A[], int n) {
int i;
printf("The list is:\n ");
for (i = 0; i < n; i++)
printf("%d ", A[i]);
printf("\n");
} /* 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, merge local list with other lists
* using tree structured communication
* Input args: global_n, my_rank, p, comm
* In/out args: local_A, local_n_p
* Notes: local_n_p tracks the size of local_A, which doubles
* with each iteration
*/
void Sort(int local_A[], int global_n, int* local_n_p, int my_rank,
int p, MPI_Comm comm) {
int partner;
int done = 0;
unsigned bitmask = 1;
MPI_Status status;
/* Sort local list using built-in quick sort */
qsort(local_A, *local_n_p, sizeof(int), Compare);
while (!done && bitmask < p) {
partner = my_rank ^ bitmask;
# ifdef DEBUG
printf("Proc %d > partner = %d, bitmask = %d\n",
my_rank, partner, bitmask);
# endif
if (my_rank < partner) {
MPI_Recv(local_A + *local_n_p, *local_n_p, MPI_INT,
partner, 0, comm, &status);
/* Note that *local_n_p is updated by Merge */
Merge(local_A, local_A + *local_n_p, local_n_p);
# ifdef DEBUG
Print_local_lists(local_A, *local_n_p, bitmask, my_rank, p, comm);
# endif
bitmask <<= 1;
} else {
MPI_Send(local_A, *local_n_p, MPI_INT, partner, 0, comm);
done = 1;
}
} /* while */
} /* Sort */
/*-------------------------------------------------------------------
* Function: Merge
* Purpose: Merge the sorted lists in local_A and local_B, each
* of which has size *local_n_p. Sorted list returned
* returned in local_A, local_B overwritten, local_n_p
* doubled in size.
* In/out args: all
* Notes:
* 1. local_A is a pointer to a list of length
* at least 2*(*local_n_p)
* 2. local_B points to local_A+*local_n_p. So it is overwritten
* when the sorted list is copied into local_A.
* 3. On return, local_n_p points to an int storing the new length
* of local_A -- twice the length of the input local_A.
*/
void Merge(int local_A[], int local_B[], int* local_n_p) {
int ai, bi, ci, i;
int *C;
int csize = 2*(*local_n_p);
C = (int*) malloc(csize*sizeof(int));
ai = 0;
bi = 0;
ci = 0;
while ((ai < *local_n_p) && (bi < *local_n_p)) {
if (local_A[ai] <= local_B[bi]) {
C[ci] = local_A[ai];
ci++; ai++;
} else {
C[ci] = local_B[bi];
ci++; bi++;
}
}
if (ai >= *local_n_p)
for (i = ci; i < csize; i++, bi++)
C[i] = local_B[bi];
else if (bi >= *local_n_p)
for (i = ci; i < csize; i++, ai++)
C[i] = local_A[ai];
for (i = 0; i < csize; i++)
local_A[i] = C[i];
free(C);
*local_n_p = 2*(*local_n_p);
} /* merge */
/*-------------------------------------------------------------------
* 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
* 2. bitmask is 0 if all the processes in comm are involved,
* otherwise set increment = 2*bitmask, and processes
* 0, increment, 2*increment, 3*increment, . . .
* participate
*/
void Print_local_lists(int local_A[], int local_n, unsigned bitmask,
int my_rank, int p, MPI_Comm comm) {
int* A;
int q;
MPI_Status status;
int increment;
if (bitmask == 0)
increment = 1;
else
increment = 2*bitmask;
if (my_rank == 0) {
A = (int*) malloc(local_n*sizeof(int));
Print_list(local_A, local_n, my_rank);
for (q = increment; q < p; q += increment) {
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 */