Generate mandelbrot images using many clustered computers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
6.0 KiB

  1. #include<mpi.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<stdbool.h>
  6. #include<math.h>
  7. #include <time.h>
  8. #include <unistd.h>
  9. #include "types.h"
  10. #include "ezpng.h"
  11. #include "font.h"
  12. void renderFrame(int iteration, Pixel *buffer, Config conf, int numSlaves)
  13. {
  14. long curPixel = 0;
  15. long pixelsDone = 0;
  16. //Send config to slaves
  17. for(int i = 1; i < numSlaves; i++)
  18. {
  19. long notApixel = -2;
  20. MPI_Send(&notApixel, 1, MPI_LONG, i, 1, MPI_COMM_WORLD);
  21. MPI_Send(&conf, sizeof(conf), MPI_BYTE, i, 0, MPI_COMM_WORLD);
  22. }
  23. long lastCheckpoint = 0;
  24. long busy[numSlaves]; //Holds id of pixel being worked on
  25. Pixel tmpBufs[numSlaves][conf.batch];
  26. MPI_Request reqs[numSlaves];
  27. for(int i = 0; i < numSlaves; i++)
  28. {
  29. busy[i] = -1;
  30. }
  31. bool done = false;
  32. long startTime, lastTime, curTime;
  33. startTime = time(NULL);
  34. lastTime = startTime;
  35. while(!done)
  36. {
  37. done = curPixel >= (long)conf.w * conf.h;
  38. curTime = time(NULL);
  39. if(curTime > lastTime && pixelsDone > 0)
  40. {
  41. lastTime = curTime;
  42. double percent = (double)pixelsDone / conf.w / conf.h * 100;
  43. int delta = curTime - startTime;
  44. int eta = (100 - percent) * delta / percent;
  45. printf("\r\x1b[AFrame %05d - %02d%% \t\t\tETA: %d seconds \r\n", iteration, (int)percent, eta);
  46. }
  47. for(int i = 1; i < numSlaves; i++)
  48. {
  49. if(busy[i] != -1) done = false;
  50. //Find slaves that are not busy, and send them stuff to do
  51. if((busy[i] == -1) && curPixel < (long)conf.w * conf.h)
  52. {
  53. long amtToSend = MIN(conf.batch, ((long)conf.w * conf.h) - curPixel);
  54. MPI_Send(&curPixel, 1, MPI_LONG, i, 1, MPI_COMM_WORLD);
  55. MPI_Send(buffer + curPixel, amtToSend * sizeof(Pixel),
  56. MPI_BYTE, i, 0, MPI_COMM_WORLD);
  57. //Start recv
  58. MPI_Irecv(tmpBufs[i], amtToSend * sizeof(Pixel), MPI_BYTE,
  59. i, 0, MPI_COMM_WORLD, &reqs[i]);
  60. busy[i] = curPixel;
  61. curPixel += conf.batch; //{batch} pixels at a time
  62. }
  63. else if(reqs[i] != 0)
  64. {
  65. //Try to recv data from the slaves
  66. int amtToRecv = MIN(conf.batch, ((long)conf.w * conf.h) - busy[i]);
  67. int flag = 0;
  68. MPI_Test(&reqs[i], &flag, MPI_STATUS_IGNORE);
  69. if(flag != 0)
  70. {
  71. //printf("%d, %d\r\n", amtToRecv, i);
  72. //Buf is valid
  73. memcpy(buffer + busy[i], tmpBufs[i], sizeof(Pixel) * amtToRecv);
  74. pixelsDone += conf.batch;
  75. busy[i] = -1;
  76. reqs[i] = 0;
  77. }
  78. }
  79. }
  80. }
  81. }
  82. void zoomToPoint(char *coordFilename, int numSlaves, Config conf)
  83. {
  84. FILE *fp = fopen(coordFilename, "r");
  85. if(fp == NULL)
  86. {
  87. printf("Couldn't open coordinate file\r\n");
  88. exit(1);
  89. }
  90. if(
  91. fscanf(fp, "%32000s", conf.cX_str) != 1 ||
  92. fscanf(fp, "%32000s", conf.cY_str) != 1
  93. )
  94. {
  95. printf("Format of the coordinate file doesn't make sense\r\n");
  96. printf("Real number should be on line 1\r\nImaginary should be on line 2\r\n");
  97. exit(1);
  98. }
  99. fclose(fp);
  100. printf("Starting zoom with the following parameters:\r\n");
  101. printf(ANSI_COLOR_RESET "Image dimensions: " ANSI_COLOR_GREEN "(%d, %d)\r\n", conf.w, conf.h);
  102. printf(ANSI_COLOR_RESET "Zoom: " ANSI_COLOR_GREEN "%lf^%d\r\n", conf.zoomBase, conf.zoomExp);
  103. printf(ANSI_COLOR_RESET "Max iterations: " ANSI_COLOR_GREEN "%d\r\n", conf.maxIter);
  104. printf(ANSI_COLOR_RESET "Batch size: " ANSI_COLOR_GREEN "%d\r\n", conf.batch);
  105. printf(ANSI_COLOR_RESET "Center: " ANSI_COLOR_GREEN "(%.8s, %.8s)\r\n" ANSI_COLOR_RESET,
  106. conf.cX_str, conf.cY_str);
  107. FBuffer buffer;
  108. buffer.w = conf.w;
  109. buffer.h = conf.h;
  110. buffer.buf = malloc(sizeof(Pixel) * conf.w * conf.h);
  111. printf("\r\n");
  112. for(int i = 0; i < 1000; i++)
  113. {
  114. conf.zoomExp--;
  115. renderFrame(i, buffer.buf, conf, numSlaves);
  116. char filename[30];
  117. strcpy(filename, "img/");
  118. sprintf(filename + 4, "%d", i);
  119. strcat(filename, ".png");
  120. //a^b = 10^c
  121. //c = bloga
  122. char infoStr[100];
  123. sprintf(infoStr, "Zoom: 1E%.2lf", (-conf.zoomExp * log10(conf.zoomBase)));
  124. bufPrint(buffer, 4, 12, infoStr,
  125. (Pixel){.red = 255, .green = 255, .blue = 255});
  126. //This is a potential bottleneck
  127. //All processes pause while they wait for this to finish
  128. savePng(filename, conf.h, conf.w, buffer.buf);
  129. }
  130. //Send all of the threads a curPixel of -1
  131. //This tells them to stop
  132. for(int i = 1; i < numSlaves; i++)
  133. {
  134. long curPixel = -1;
  135. MPI_Send(&curPixel, 1, MPI_LONG, i, 1, MPI_COMM_WORLD);
  136. }
  137. free(buffer.buf);
  138. printf("Done!\r\n");
  139. }
  140. void prnHelp(char *name)
  141. {
  142. printf("Usage: %s [OPTION]\r\n", name);
  143. printf("Available options:\r\n");
  144. printf("h\t\tThis screen\r\n");
  145. printf("W WDTH\t\tOutput image width\r\n");
  146. printf("H HGHT\t\tOutput image height\r\n");
  147. printf("i ITER\t\tMaximum # of iterations\r\n");
  148. printf("z ZOOMBASE\tZoom base\r\n");
  149. printf("e ZOOMEXP\tZoom exp\r\n");
  150. printf("zoom = ZOOMBASE^ZOOMEXP\r\n");
  151. printf("ZOOMEXP++ for each iteration\r\n");
  152. printf("b BATCHSIZE\t# pixels requested at once from each node\r\n");
  153. printf("c CENTERFILE\tFile holding real/imaginary values to zoom to\r\n");
  154. exit(0);
  155. }
  156. void master_main(char argc, char **argv, int numSlaves)
  157. {
  158. printf("\t\tMPI Mandelbrot Generator\r\n");
  159. printf(ANSI_COLOR_YELLOW "\t\t\twww.scd31.com\r\n" ANSI_COLOR_RESET);
  160. Config conf = {
  161. .w = 120,
  162. .h = 120,
  163. .maxIter = 10000,
  164. .zoomBase = 10,
  165. .zoomExp = -995,
  166. .batch = 1, //breaks if batch is too big relative to w*h
  167. };
  168. char filename[501];
  169. bool hasCenter = false;
  170. char c;
  171. while((c = getopt(argc, argv, "W:H:i:z:e:b:hc:")) != -1)
  172. {
  173. switch(c)
  174. {
  175. case 'h': prnHelp(argv[0]); break;
  176. case 'W': conf.w = strtol(optarg, NULL, 0); break;
  177. case 'H': conf.h = strtol(optarg, NULL, 0); break;
  178. case 'i': conf.maxIter = strtol(optarg, NULL, 0); break;
  179. case 'z': conf.zoomBase = atof(optarg); break;
  180. case 'e': conf.zoomExp = -strtol(optarg, NULL, 0); break;
  181. case 'b': conf.batch = strtol(optarg, NULL, 0); break;
  182. case 'c':
  183. if(strlen(optarg) >= 500)
  184. {
  185. printf("Center filename too long!\r\n");
  186. exit(1);
  187. }
  188. strcpy(filename, optarg);
  189. hasCenter = true;
  190. }
  191. }
  192. if(!hasCenter)
  193. {
  194. printf("ERROR: You need to specify a file with the coordinates of the center point\r\n");
  195. prnHelp(argv[0]);
  196. exit(1);
  197. }
  198. zoomToPoint(filename, numSlaves, conf);
  199. }