csci5451/assignments/02/qs_mpi.c
2023-10-30 03:04:21 +00:00

193 lines
No EOL
5.8 KiB
C

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#define ORDER_FORWARDS 1
#define ORDER_BACKWARDS 2
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define GENERIC_MIN(x, y) ((x) < (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int : (i))
#define ENSURE_float(f) _Generic((f), float : (f))
#define MAX(type, x, y) (type) GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
#define MIN(type, x, y) (type) GENERIC_MIN(ENSURE_##type(x), ENSURE_##type(y))
void local_quicksort(int *arr, int lo, int hi);
char *string_of_list(int *arr, int len);
int main(int argc, char **argv) {
int rank, p;
MPI_Init(&argc, &argv);
int n = atoi(argv[1]);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &p);
// Generate integers
int n_over_p = n / p;
int integers[n_over_p];
// Important implementation detail: srand(0) is specially handled by glibc to
// behave as if it was called with srand(1). To get around this, I'm seeding
// with rank + 1
//
// See more: https://stackoverflow.com/a/27386563
srand(rank + 1);
for (int i = 0; i < n_over_p; ++i) {
// TODO: For readability during debugging, I'm capping this
integers[i] = rand() % 101;
// printf(" - %d\n", integers[i]);
}
int group_root = 0;
// Locally sort
printf("[%d] Numbers before: %s\n", rank,
string_of_list(integers, n_over_p));
local_quicksort(integers, 0, n_over_p);
printf("[%d] Numbers after first sort: %s\n", rank,
string_of_list(integers, n_over_p));
// Select a pivot.
// This pivot is broadcasted to all nodes
int pivot;
// The pivot is selected as the median (see chp. 9.4.4)
// Not the real median though, need an existing element of the array
pivot = integers[n_over_p / 2];
MPI_Bcast(&pivot, 1, MPI_INT, 0, MPI_COMM_WORLD);
printf("Median: %d\n", pivot);
// Determine where the boundary between S (lower) and L (higher) lies
int boundary;
for (int i = 0; i < n_over_p; ++i) {
if (integers[i] >= pivot) {
boundary = i;
break;
}
}
int S_lo = 0, S_hi = boundary - 1;
int L_lo = boundary, L_hi = n_over_p - 1;
int S_size = S_hi - S_lo + 1, L_size = L_hi - L_lo + 1;
printf("[%d] S: [%d - %d] (%d), L: [%d - %d] (%d)\n", rank, S_lo, S_hi,
S_size, L_lo, L_hi, L_size);
// Perform global arrangement
int S_global_end, L_reverse_end;
MPI_Scan(&S_size, &S_global_end, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
MPI_Scan(&L_size, &L_reverse_end, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
int S_global_start = S_global_end - S_size,
L_reverse_start = L_reverse_end - L_size,
L_global_start = n - L_reverse_end, L_global_end = n - L_reverse_start;
printf("[%d] S: [%d - %d], L: [%d - %d]\n", rank, S_global_start,
S_global_end - 1, L_global_start, L_global_end - 1);
// Send it to the correct target
int S_starting_process = S_global_start / n_over_p;
int S_offset = S_global_start % n_over_p,
L_offset = L_global_start % n_over_p;
int i, local_start;
for (i = S_starting_process, local_start = 0; i * n_over_p < S_global_end;
++i) {
int processor_start = S_offset;
int mod = S_global_end % n_over_p;
int processor_end = MIN(int, n_over_p, mod == 0 ? n_over_p : mod);
int total_start = i * n_over_p + processor_start;
int total_end = i * n_over_p + processor_end;
int n_bytes = total_end - total_start;
int local_end = local_start + n_bytes;
printf("[%d] - sending %d elements from local S [%d..%d] to destination S "
"[%d..%d]\n",
rank, n_bytes, local_start, local_end, processor_start,
processor_end);
// MPI_Sendrecv(const void *sendbuf, int sendcount, MPI_Datatype sendtype,
// int dest, int sendtag, void *recvbuf, int recvcount,
// MPI_Datatype recvtype, int source, int recvtag, MPI_Comm
// comm, MPI_Status *status)
local_start += n_bytes;
}
int L_starting_process = L_global_start / n_over_p;
for (i = L_starting_process; i * n_over_p < L_global_end; ++i) {
int processor_start = L_offset;
int mod = L_global_end % n_over_p;
int processor_end = MIN(int, n_over_p, mod == 0 ? n_over_p : mod);
int total_start = i * n_over_p + processor_start;
int total_end = i * n_over_p + processor_end;
int n_bytes = total_end - total_start;
int local_end = local_start + n_bytes;
printf("[%d] - sending %d elements from local L [%d..%d] to destination L "
"[%d..%d]\n",
rank, n_bytes, local_start, local_end, processor_start,
processor_end);
}
// The first node is responsible for collecting all the data and then printing
// it out to the file
// MPI_Gather(const void *sendbuf, int sendcount, MPI_INT, void *recvbuf,
// int recvcount, MPI_INT, 0, MPI_COMM_WORLD);
if (rank == 0) {
FILE *f = fopen(argv[2], "w");
fclose(f);
}
MPI_Finalize();
return 0;
}
// hi not inclusive
void local_quicksort(int *arr, int lo, int hi) {
int temp;
if (lo >= hi || lo < 0)
return;
int pivot = arr[hi - 1];
int pivot_idx = lo - 1;
for (int j = lo; j < hi; ++j) {
if (arr[j] < pivot) {
pivot_idx += 1;
temp = arr[j];
arr[j] = arr[pivot_idx];
arr[pivot_idx] = temp;
}
}
pivot_idx += 1;
temp = arr[hi - 1];
arr[hi - 1] = arr[pivot_idx];
arr[pivot_idx] = temp;
// Recursive call
local_quicksort(arr, lo, pivot_idx);
local_quicksort(arr, pivot_idx + 1, hi);
}
char *string_of_list(int *arr, int len) {
char *buffer = malloc(1000);
int offset = 0; // Keep track of the current position in the buffer
for (int i = 0; i < len; i++) {
offset += sprintf(buffer + offset, "%d", arr[i]);
if (i < len - 1) {
// Add a separator (e.g., comma or space) if it's not the last element
offset += sprintf(buffer + offset, " ");
}
}
return buffer;
}