/* File: mpi_primes_sort.c * Purpose: Find all primes less than or equal to an input value * * Input: n: integer >= 2 (from command line) * Output: Sorted list of primes between 2 and n, * * Compile: mpicc -g -Wall -o mpi_primes_sort mpi_primes_sort.c -lm * Usage: mpiexec -n

./mpi_primes_sort * p: number of MPI processes * n: max int to test for primality * * Note: * 1. DEBUG compile flag for verbose output */ #include #include #include #include #include const int STRING_MAX = 1000; int Get_n(int argc, char* argv[], int my_rank, int p, MPI_Comm comm); int Is_prime(int i); void Print_primes(int my_primes[], int my_prime_count, int my_rank, int p, MPI_Comm comm); void Merge_lists(int my_contrib[], int my_count, int** list_p, int* size_p, int my_rank, int p, MPI_Comm comm); void Merge(int** a_p, int* asize_p, int b[], int bsize, int** c_p); void Compute_list_sizes(int prime_counts[], int recv_counts[], int p); void Print_list(char* title, int list[], int n, int my_rank); int main(int argc, char* argv[]) { int n, i, p, local_n, incr; int my_rank; // my process rank MPI_Comm comm; int* my_primes, my_prime_count=0; MPI_Init(&argc, &argv); comm = MPI_COMM_WORLD; MPI_Comm_size(comm, &p); MPI_Comm_rank(comm, &my_rank); n = Get_n(argc, argv, my_rank, p, comm); local_n = n/(2*p)+2; my_primes = malloc(local_n*sizeof(int)); if (my_rank == 0) my_primes[my_prime_count++] = 2; incr = 2*p; for (i = 2*my_rank + 3; i <= n; i += incr) if (Is_prime(i)) { my_primes[my_prime_count++] = i; # ifdef DEBUG printf("Proc %d > %d\n", my_rank, i); # endif } Print_list("After prime finder", my_primes, my_prime_count, my_rank); Print_primes(my_primes, my_prime_count, my_rank, p, comm); free(my_primes); MPI_Finalize(); return 0; } /* main */ /*------------------------------------------------------------------- * Function: Get_n * Purpose: Get the input value n * Input args: my_rank: process rank in comm * p: number of processes in comm * comm: communicator used by program * argc: number of command line args * argv: array of command line args */ int Get_n(int argc, char* argv[], int my_rank, int p, MPI_Comm comm) { int n; if (my_rank == 0) { if (argc != 2) n = -1; // error else { n = strtol(argv[1], NULL, 10); } } MPI_Bcast(&n, 1, MPI_INT, 0, comm); /* Check for bogus input */ if (n <= 1) { if (my_rank == 0) { fprintf(stderr, "usage: mpiexec -n

%s \n", argv[0]); fprintf(stderr, " p = number of MPI processes\n"); fprintf(stderr, " n = max integer to test for primality (>= 2)\n"); } MPI_Finalize(); exit(0); } return n; } /* Get_n */ /*------------------------------------------------------------------- * Function: Is_prime * Purpose: Determine whether the argument is prime * Input arg: i * Return val: true (nonzero) if arg is prime, false (zero) otherwise */ int Is_prime(int i) { int j; for (j = 2; j <= sqrt(i); j++) if (i % j == 0) return 0; return 1; } /* Is_prime */ /*------------------------------------------------------------------- * Function: Print_primes * Purpose: Print the primes found by the processes * Input args: my_primes: the primes found by the current process * my_prime_count: the number of primes found by the * current process * my_rank, p, comm: the usual MPI variables. */ void Print_primes(int my_primes[], int my_prime_count, int my_rank, int p, MPI_Comm comm) { int* all_primes; int all_primes_count, i; Merge_lists(my_primes, my_prime_count, &all_primes, &all_primes_count, my_rank, p, comm); if (my_rank == 0) { printf("The primes are\n"); for (i = 0; i < all_primes_count; i++) printf("%d ", all_primes[i]); printf("\n"); free(all_primes); } } /* Print_primes */ /*------------------------------------------------------------------- * Function: Merge_lists * Purpose: Merge a collection of sorted lists, one per process. * In args: my_contrib: my sorted list * my_count: the number of elements in my sorted list * my_rank, p, comm: the usual MPI variables * Out args: list_p: the merged list * size_p: number of elements in the merged list * */ void Merge_lists(int my_contrib[], int my_count, int** list_p, int* size_p, int my_rank, int p, MPI_Comm comm) { int my_size, my_recv_size, done = 0, partner, i, curr_size; int* my_list = NULL, *recv_list = NULL, *temp = NULL; unsigned bitmask = 1; int *counts = malloc(p*sizeof(int)); int *recv_counts = malloc(p*sizeof(int)); MPI_Status status; /* Needed by valgrind wrappers */ int recv_count; /* Used during debugging */ MPI_Allgather(&my_count, 1, MPI_INT, counts, 1, MPI_INT, comm); Print_list("list sizes", counts, p, my_rank); # ifdef DEBUG if (my_rank == 0) { printf("Counts after allgather:\n"); for (i = 0; i < p; i++) printf("%d ", counts[i]); printf("\n"); } # endif Compute_list_sizes(counts, recv_counts, p); # ifdef DEBUG if (my_rank == 0) { printf("Counts after Compute_list_sizes:\n"); for (i = 0; i < p; i++) printf("%d ", counts[i]); printf("\n"); printf("Recv_counts after Compute_list_sizes:\n"); for (i = 0; i < p; i++) printf("%d ", recv_counts[i]); printf("\n"); } # endif Print_list("recv counts", recv_counts, p, my_rank); my_size = counts[my_rank]; if (my_size > 0) my_list = malloc(my_size*sizeof(int)); if (my_size > 0) temp = malloc(my_size*sizeof(int)); my_recv_size = recv_counts[my_rank]; if (my_recv_size > 0) recv_list = malloc(my_recv_size*sizeof(int)); for (i = 0; i < my_count; i++) my_list[i] = my_contrib[i]; curr_size = my_count; while (!done && bitmask < p) { partner = my_rank ^ bitmask; if (my_rank < partner && counts[partner] > 0) { if (partner < p) { MPI_Recv(recv_list, counts[partner], MPI_INT, partner, 0, comm, &status); // MPI_Recv(recv_list, my_recv_size, MPI_INT, // partner, 0, comm, &status); // MPI_Get_count(&status, MPI_INT, &recv_count); // for (i = curr_size; i < curr_size + counts[partner]; i++) // my_list[i] = recv_list[i-curr_size]; // curr_size += counts[partner]; // Merge(&my_list, &curr_size, recv_list, counts[partner], // &temp); recv_count = counts[partner]; # ifdef DEBUG printf("Proc %d > received %d from %d, predicted %d\n", my_rank, recv_count, partner, counts[partner]); fflush(stdout); # endif Merge(&my_list, &curr_size, recv_list, recv_count, &temp); Print_list("after merge", my_list, curr_size, my_rank); } bitmask <<= 1; } else { # ifdef DEBUG printf("Proc %d > send to = %d, bitmask = %d, my_size = %d\n", my_rank, partner, bitmask, my_size); # endif MPI_Send(my_list, my_size, MPI_INT, partner, 0, comm); done = 1; } } if (my_rank == 0) { *list_p = my_list; *size_p = my_size; } else if (my_list != NULL) free(my_list); if (recv_list != NULL) free(recv_list); if (temp != NULL) free(temp); } /* Merge_lists */ /*------------------------------------------------------------------- * Function: Merge * Purpose: Merge two sorted lists * In/out arg: a: on input one of the sorted lists * on return the combined list * asize_p: on input number of elements in a * on return number of elements in updated a * In args: b: the other input list * bsize: the number of elements in b * c: temporary storage for the merged list */ void Merge(int** a_p, int* asize_p, int b[], int bsize, int** c_p) { int *a = *a_p; int *c = *c_p; int ai, bi, ci; ai = bi = ci = 0; while (ai < *asize_p && bi < bsize) if (a[ai] <= b[bi]) c[ci++] = a[ai++]; else c[ci++] = b[bi++]; while (ai < *asize_p) c[ci++] = a[ai++]; while (bi < bsize) c[ci++] = b[bi++]; // memcpy(a, c, ci*sizeof(int)); *a_p = c; *c_p = a; *asize_p = ci; } /* Merge */ /*------------------------------------------------------------------- * Function: Compute_list_sizes * Purpose: Find the sizes of the lists of primes each process * will ultimately store. Also compute the maximum * size list each process will receive in any one * call to MPI_Recv. * Input args: prime_counts: the qth entry is the number of primes * found by process q * p: the number of processes and the size of prime_counts */ void Compute_list_sizes(int prime_counts[], int recv_counts[], int p) { unsigned bitmask; int partner, rank, incr; # ifdef LIST_SIZE_DEBUG int my_rank; MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); # endif for (rank = 0; rank < p; rank++) recv_counts[rank] = 0; for (bitmask = 1, incr = 2; bitmask < p; bitmask <<= 1, incr *= 2) { for (rank = 0; rank < p; rank += incr) { partner = rank ^ bitmask; if (partner < p) { prime_counts[rank] += prime_counts[partner]; if (recv_counts[rank] < prime_counts[partner]) recv_counts[rank] = prime_counts[partner]; } } # ifdef LIST_SIZE_DEBUG if (my_rank == 0) { printf("After bitmask = %d, prime_counts =\n", bitmask); for (rank = 0; rank < p; rank++) printf("%d ", prime_counts[rank]); printf("\n"); printf("After bitmask = %d, recv_counts =\n", bitmask); for (rank = 0; rank < p; rank++) printf("%d ", recv_counts[rank]); printf("\n"); } # endif } } /* Compute_list_size */ /*------------------------------------------------------------------- * Function: Print_list * Purpose: Convert a list of ints to a single string before * printing. This should make it less likely that the * output is interrupted by another process. This is * mainly intended for debugging purposes. * In args: title: title of list * list: the ints to be printed * n: the number of ints * my_rank: the usual MPI variable */ void Print_list(char* title, int list[], int n, int my_rank) { char string[STRING_MAX]; char* s_p; int i; sprintf(string, "Proc %d %s > ", my_rank, title); // Pointer arithmetic: make s_p point to the character strlen(string) // into string; i.e., make it point at the `\0' s_p = string + strlen(string); for (i = 0; i < n; i++) { sprintf(s_p, "%d ", list[i]); s_p = string + strlen(string); } printf("%s\n", string); fflush(stdout); } /* Print_list */