From a43f2ea5cdceb3883eca41fd67998a29b6ff6d2d Mon Sep 17 00:00:00 2001 From: josh Date: Mon, 8 Dec 2008 18:08:16 +0000 Subject: [PATCH] added mpi-fractals-static.cc from mpi-fractals.cc git-svn-id: svn://anubis/gvsu@338 45c1a28c-8058-47b2-ae61-ca45b979098e --- cs677/final/mpi-fractals-static.cc | 379 +++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 cs677/final/mpi-fractals-static.cc diff --git a/cs677/final/mpi-fractals-static.cc b/cs677/final/mpi-fractals-static.cc new file mode 100644 index 0000000..5de6339 --- /dev/null +++ b/cs677/final/mpi-fractals-static.cc @@ -0,0 +1,379 @@ +/* + * Josh Holtrop + * 2008-12-11 + * CS677 Final Project + * This program implements a fractal-image generator and viewer + * that uses OpenMPI and OpenMP. + */ + +#include +#include +#include /* struct timeval, gettimeofday() */ +#include +#include +#include "Computation.h" +#include "NewtonComputation.h" +#include "FatouComputation.h" +using namespace std; + +#define PROGNAME "Josh's CS677 Final : MPI Fractal Generator" +#define getXVirt(x) (((x) - (width >> 1)) * zoom + x_center) +#define getYVirt(y) ((-((y) - (height >> 1))) * zoom + y_center) + +/************************************************************************** + * Utility functions * + *************************************************************************/ +bool createWindow(int width, int height, + SDL_Surface ** screen, Uint32 ** pixels); +void getSizes(int * rank, int * size, int * nprocs); +void draw(int rank, int world_size, int nprocs, int width, int height, + Uint32 * pixels, Uint32 * taskVals, Computation * computation); +void sendWindowVals(double * winVals, int world_size); + +/************************************************************************** + * Global variables * + *************************************************************************/ +static double x_center = 0.0; +static double y_center = 0.0; +static double zoom = 1/300.0; +/* a "task" will be processing task_size pixels */ +static int task_size = 100; + +/************************************************************************** + * This is the main entry point for our program * + *************************************************************************/ +int main(int argc, char * argv[]) +{ + int width = 600; + int height = 600; + int my_rank = 0; + int world_size = 0; + int nprocs = 0; + Computation * computation = NULL; + int fractal_type = 0; + bool display_times = false; + + SDL_Surface * screen; + Uint32 * pixels; + + MPI_Init(&argc, &argv); + for (int i = 1; i < argc; i++) + { + if (!strncmp(argv[i], "-w", 2)) + { + width = atoi(strlen(argv[i]) > 2 ? argv[i] + 2 : argv[++i]); + } + else if (!strncmp(argv[i], "-h", 2)) + { + height = atoi(strlen(argv[i]) > 2 ? argv[i] + 2 : argv[++i]); + } + else if (!strncmp(argv[i], "-t", 2)) + { + fractal_type = atoi(strlen(argv[i]) > 2 ? argv[i] + 2 : argv[++i]); + } + else if (!strcmp(argv[i], "--times")) + { + display_times = true; + } + else if (!strcmp(argv[i], "--no-threads")) + { + omp_set_num_threads(1); + } + else if (!strncmp(argv[i], "-s", 2)) + { + task_size = atoi(strlen(argv[i]) > 2 ? argv[i] + 2 : argv[++i]); + } + } + getSizes(&my_rank, &world_size, &nprocs); + if (my_rank == 0) + { + char hostname[1000]; + gethostname(&hostname[0], 1000); + cout << "Master hostname: " << hostname << endl; + } + + switch (fractal_type) + { + case 0: + default: + computation = new NewtonComputation(); + x_center = 0.0; + y_center = 0.0; + zoom = 2.0 / width; + break; + case 1: + computation = new FatouComputation(); + x_center = 3.001; + y_center = 0.075975; + zoom = 2.0 / width; + break; + } + + unsigned int * taskVals = new unsigned int[task_size + 1]; + double window_vals[4]; + if (my_rank == 0) + { + SDL_Event event; + bool going = true; + bool window_success = createWindow(width, height, &screen, &pixels); + bool redraw = true; + + if (!window_success) + going = false; + + /* master loop */ + while (going && SDL_WaitEvent(&event) != 0) + { + if (redraw) + { + struct timeval before, after; + window_vals[0] = 0.0; + window_vals[1] = x_center; + window_vals[2] = y_center; + window_vals[3] = zoom; + sendWindowVals(&window_vals[0], world_size); + gettimeofday(&before, NULL); + draw(my_rank, world_size, nprocs, width, height, + pixels, taskVals, computation); + gettimeofday(&after, NULL); + if (display_times) + { + double time_before = before.tv_sec + before.tv_usec / 1000000.0; + double time_after = after.tv_sec + after.tv_usec / 1000000.0; + double diff = time_after - time_before; + cout << "Elapsed time: " << diff << " seconds." << endl; + } + redraw = false; + } + SDL_UpdateRect(screen, 0, 0, 0, 0); + switch (event.type) + { + case SDL_QUIT: + going = false; + break; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_q) + going = false; + break; + case SDL_MOUSEBUTTONDOWN: + switch (event.button.button) + { + case 1: /* left-click to re-center and zoom in */ + x_center = getXVirt(event.button.x); + y_center = getYVirt(event.button.y); + zoom /= 2.0; + redraw = true; + break; + case 2: /* middle click just to re-center */ + x_center = getXVirt(event.button.x); + y_center = getYVirt(event.button.y); + redraw = true; + break; + case 4: /* zoom in */ + zoom /= 2.0; + redraw = true; + break; + case 5: /* zoom out */ + zoom *= 2.0; + redraw = true; + break; + } + break; + } + } + window_vals[0] = 1.0; + sendWindowVals(&window_vals[0], world_size); + } + else + { + /* slave loop */ + for (;;) + { + // DEBUG: +// cout << "MPI node " << my_rank << " waiting for command." << endl; + /* wait for a redraw or quit command */ + MPI_Recv(&window_vals[0], 4, MPI_DOUBLE, + MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, NULL); + if (window_vals[0] != 0.0) + break; + x_center = window_vals[1]; + y_center = window_vals[2]; + zoom = window_vals[3]; + // DEBUG: +// cout << "MPI node " << my_rank << " received (" +// << x_center << ", " << y_center << "), zoom " << zoom << endl; + draw(my_rank, world_size, nprocs, width, height, + NULL, taskVals, computation); + } + } + delete[] taskVals; + + MPI_Finalize(); + + delete computation; + return 0; +} + +/************************************************************************** + * This utility function is used by the master MPI node to create a * + * window using SDL for displaying the fractal in and getting user input. * + *************************************************************************/ +bool createWindow(int width, int height, + SDL_Surface ** screen, Uint32 ** pixels) +{ + if (SDL_Init(SDL_INIT_VIDEO)) + { + cerr << "Failed to initialize SDL!" << endl; + return false; + } + + atexit(SDL_Quit); + + if (!(*screen = SDL_SetVideoMode(width, height, 32, 0))) + { + cerr << "Failed to set video mode!" << endl; + return false; + } + SDL_WM_SetCaption(PROGNAME, PROGNAME); + + *pixels = (Uint32 *) (*screen)->pixels; + + return true; +} + +/************************************************************************** + * This utility function returns the MPI node's rank, the total number * + * of MPI nodes, and the number of processing cores on the local node * + *************************************************************************/ +void getSizes(int * rank, int * size, int * nprocs) +{ + MPI_Comm_rank(MPI_COMM_WORLD, rank); + MPI_Comm_size(MPI_COMM_WORLD, size); + + *nprocs = sysconf(_SC_NPROCESSORS_CONF); + + int displs[*size]; + int counts[*size]; + for (int i = 0; i < *size; i++) + { + displs[i] = i; + counts[i] = 1; + } + int all_nprocs[*size]; + MPI_Gatherv(nprocs, 1, MPI_INT, + &all_nprocs[0], &counts[0], &displs[0], MPI_INT, + 0, MPI_COMM_WORLD); + + if (*rank == 0) + { + int total_nprocs = 0; + cout << "Number of cores on each MPI node:" << endl; + for (int i = 0; i < *size; i++) + { + cout << all_nprocs[i] << " "; + total_nprocs += all_nprocs[i]; + } + cout << endl; + cout << "Total number of cores: " << total_nprocs << endl; + } +} + +/************************************************************************** + * This function is executed by each MPI node every time a fractal * + * frame is to be drawn. * + *************************************************************************/ +void draw(int rank, int world_size, int nprocs, int width, int height, + Uint32 * pixels, Uint32 * taskVals, Computation * computation) +{ + // DEBUG: +// cout << "In draw() with rank " << rank << endl; + MPI_Status mpi_status; + if (world_size == 1) + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + double x_virt = getXVirt(x); + double y_virt = getYVirt(y); + *pixels++ = computation->compute(x_virt, y_virt); + } + } + } + else if (rank == 0) + { + int done_val = -1; + int num_pixels = width * height; + int pixel = 0; + for (int to_proc = 1; to_proc < world_size; to_proc++) + { + if (pixel < num_pixels) + { + MPI_Send(&pixel, 1, MPI_INT, to_proc, 0, MPI_COMM_WORLD); + pixel += task_size; + } + else + { + MPI_Send(&done_val, 1, MPI_INT, to_proc, 0, MPI_COMM_WORLD); + } + } + int num_tasks = (num_pixels + task_size - 1) / task_size; + for (int received = 0; received < num_tasks; received++) + { + MPI_Recv(taskVals, task_size + 1, MPI_INT, + MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &mpi_status); + if (pixel < num_pixels) + { + MPI_Send(&pixel, 1, MPI_INT, + mpi_status.MPI_SOURCE, 0, MPI_COMM_WORLD); + pixel += task_size; + } + else + { + MPI_Send(&done_val, 1, MPI_INT, + mpi_status.MPI_SOURCE, 0, MPI_COMM_WORLD); + } + memcpy(pixels + taskVals[0], taskVals + 1, task_size * sizeof(int)); + } + } + else + { + for (;;) + { + int pixel_num; + /* wait to be told what to do */ + MPI_Recv(&pixel_num, 1, MPI_INT, + MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, NULL); + if (pixel_num < 0) /* exit if we are done */ + break; +#pragma omp parallel for + for (int i = 0; i < task_size; i++) + { + int this_pixel_num = pixel_num + i; + int x = this_pixel_num % width; + int y = this_pixel_num / width; + double x_virt = getXVirt(x); + double y_virt = getYVirt(y); + taskVals[i + 1] = computation->compute(x_virt, y_virt); + } + /* send the computed pixel data to the master node */ + taskVals[0] = pixel_num; + MPI_Send(taskVals, task_size + 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + } + } +} + +/************************************************************************** + * This utility function is used by the master process to update all * + * of the slave processes for the position and zoom-level of the view. * + *************************************************************************/ +void sendWindowVals(double * winVals, int world_size) +{ + // DEBUG: +// cout << "Master sending out new window values" << endl; + for (int to_proc = 1; to_proc < world_size; to_proc++) + { + MPI_Send(winVals, 4, MPI_DOUBLE, to_proc, + 0, MPI_COMM_WORLD); + } +}