Generate mandelbrot images using many clustered computers

master.c 6.0KB

    #include<mpi.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<stdbool.h> #include<math.h> #include <time.h> #include <unistd.h> #include "types.h" #include "ezpng.h" #include "font.h" void renderFrame(int iteration, Pixel *buffer, Config conf, int numSlaves) { long curPixel = 0; long pixelsDone = 0; //Send config to slaves for(int i = 1; i < numSlaves; i++) { long notApixel = -2; MPI_Send(&notApixel, 1, MPI_LONG, i, 1, MPI_COMM_WORLD); MPI_Send(&conf, sizeof(conf), MPI_BYTE, i, 0, MPI_COMM_WORLD); } long lastCheckpoint = 0; long busy[numSlaves]; //Holds id of pixel being worked on Pixel tmpBufs[numSlaves][conf.batch]; MPI_Request reqs[numSlaves]; for(int i = 0; i < numSlaves; i++) { busy[i] = -1; } bool done = false; long startTime, lastTime, curTime; startTime = time(NULL); lastTime = startTime; while(!done) { done = curPixel >= (long)conf.w * conf.h; curTime = time(NULL); if(curTime > lastTime && pixelsDone > 0) { lastTime = curTime; double percent = (double)pixelsDone / conf.w / conf.h * 100; int delta = curTime - startTime; int eta = (100 - percent) * delta / percent; printf("\r\x1b[AFrame %05d - %02d%% \t\t\tETA: %d seconds \r\n", iteration, (int)percent, eta); } for(int i = 1; i < numSlaves; i++) { if(busy[i] != -1) done = false; //Find slaves that are not busy, and send them stuff to do if((busy[i] == -1) && curPixel < (long)conf.w * conf.h) { long amtToSend = MIN(conf.batch, ((long)conf.w * conf.h) - curPixel); MPI_Send(&curPixel, 1, MPI_LONG, i, 1, MPI_COMM_WORLD); MPI_Send(buffer + curPixel, amtToSend * sizeof(Pixel), MPI_BYTE, i, 0, MPI_COMM_WORLD); //Start recv MPI_Irecv(tmpBufs[i], amtToSend * sizeof(Pixel), MPI_BYTE, i, 0, MPI_COMM_WORLD, &reqs[i]); busy[i] = curPixel; curPixel += conf.batch; //{batch} pixels at a time } else if(reqs[i] != 0) { //Try to recv data from the slaves int amtToRecv = MIN(conf.batch, ((long)conf.w * conf.h) - busy[i]); int flag = 0; MPI_Test(&reqs[i], &flag, MPI_STATUS_IGNORE); if(flag != 0) { //printf("%d, %d\r\n", amtToRecv, i); //Buf is valid memcpy(buffer + busy[i], tmpBufs[i], sizeof(Pixel) * amtToRecv); pixelsDone += conf.batch; busy[i] = -1; reqs[i] = 0; } } } } } void zoomToPoint(char *coordFilename, int numSlaves, Config conf) { FILE *fp = fopen(coordFilename, "r"); if(fp == NULL) { printf("Couldn't open coordinate file\r\n"); exit(1); } if( fscanf(fp, "%32000s", conf.cX_str) != 1 || fscanf(fp, "%32000s", conf.cY_str) != 1 ) { printf("Format of the coordinate file doesn't make sense\r\n"); printf("Real number should be on line 1\r\nImaginary should be on line 2\r\n"); exit(1); } fclose(fp); printf("Starting zoom with the following parameters:\r\n"); printf(ANSI_COLOR_RESET "Image dimensions: " ANSI_COLOR_GREEN "(%d, %d)\r\n", conf.w, conf.h); printf(ANSI_COLOR_RESET "Zoom: " ANSI_COLOR_GREEN "%lf^%d\r\n", conf.zoomBase, conf.zoomExp); printf(ANSI_COLOR_RESET "Max iterations: " ANSI_COLOR_GREEN "%d\r\n", conf.maxIter); printf(ANSI_COLOR_RESET "Batch size: " ANSI_COLOR_GREEN "%d\r\n", conf.batch); printf(ANSI_COLOR_RESET "Center: " ANSI_COLOR_GREEN "(%.8s, %.8s)\r\n" ANSI_COLOR_RESET, conf.cX_str, conf.cY_str); FBuffer buffer; buffer.w = conf.w; buffer.h = conf.h; buffer.buf = malloc(sizeof(Pixel) * conf.w * conf.h); printf("\r\n"); for(int i = 0; i < 1000; i++) { conf.zoomExp--; renderFrame(i, buffer.buf, conf, numSlaves); char filename[30]; strcpy(filename, "img/"); sprintf(filename + 4, "%d", i); strcat(filename, ".png"); //a^b = 10^c //c = bloga char infoStr[100]; sprintf(infoStr, "Zoom: 1E%.2lf", (-conf.zoomExp * log10(conf.zoomBase))); bufPrint(buffer, 4, 12, infoStr, (Pixel){.red = 255, .green = 255, .blue = 255}); //This is a potential bottleneck //All processes pause while they wait for this to finish savePng(filename, conf.h, conf.w, buffer.buf); } //Send all of the threads a curPixel of -1 //This tells them to stop for(int i = 1; i < numSlaves; i++) { long curPixel = -1; MPI_Send(&curPixel, 1, MPI_LONG, i, 1, MPI_COMM_WORLD); } free(buffer.buf); printf("Done!\r\n"); } void prnHelp(char *name) { printf("Usage: %s [OPTION]\r\n", name); printf("Available options:\r\n"); printf("h\t\tThis screen\r\n"); printf("W WDTH\t\tOutput image width\r\n"); printf("H HGHT\t\tOutput image height\r\n"); printf("i ITER\t\tMaximum # of iterations\r\n"); printf("z ZOOMBASE\tZoom base\r\n"); printf("e ZOOMEXP\tZoom exp\r\n"); printf("zoom = ZOOMBASE^ZOOMEXP\r\n"); printf("ZOOMEXP++ for each iteration\r\n"); printf("b BATCHSIZE\t# pixels requested at once from each node\r\n"); printf("c CENTERFILE\tFile holding real/imaginary values to zoom to\r\n"); exit(0); } void master_main(char argc, char **argv, int numSlaves) { printf("\t\tMPI Mandelbrot Generator\r\n"); printf(ANSI_COLOR_YELLOW "\t\t\twww.scd31.com\r\n" ANSI_COLOR_RESET); Config conf = { .w = 120, .h = 120, .maxIter = 10000, .zoomBase = 10, .zoomExp = -995, .batch = 1, //breaks if batch is too big relative to w*h }; char filename[501]; bool hasCenter = false; char c; while((c = getopt(argc, argv, "W:H:i:z:e:b:hc:")) != -1) { switch(c) { case 'h': prnHelp(argv[0]); break; case 'W': conf.w = strtol(optarg, NULL, 0); break; case 'H': conf.h = strtol(optarg, NULL, 0); break; case 'i': conf.maxIter = strtol(optarg, NULL, 0); break; case 'z': conf.zoomBase = atof(optarg); break; case 'e': conf.zoomExp = -strtol(optarg, NULL, 0); break; case 'b': conf.batch = strtol(optarg, NULL, 0); break; case 'c': if(strlen(optarg) >= 500) { printf("Center filename too long!\r\n"); exit(1); } strcpy(filename, optarg); hasCenter = true; } } if(!hasCenter) { printf("ERROR: You need to specify a file with the coordinates of the center point\r\n"); prnHelp(argv[0]); exit(1); } zoomToPoint(filename, numSlaves, conf); }