multi-threaded multibrot
This commit is contained in:
		
							parent
							
								
									1b4f1b7592
								
							
						
					
					
						commit
						49b477b2de
					
				
					 1 changed files with 80 additions and 23 deletions
				
			
		
							
								
								
									
										101
									
								
								multibrot.c
									
										
									
									
									
								
							
							
						
						
									
										101
									
								
								multibrot.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,8 +1,11 @@
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
#include <complex.h>
 | 
					#include <complex.h>
 | 
				
			||||||
 | 
					#include <getopt.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <png.h>
 | 
					#include <png.h>
 | 
				
			||||||
 | 
					#include <pthread.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef uint32_t pixel_t;
 | 
					typedef uint32_t pixel_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,8 +20,7 @@ const int max_it = 170;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Compute the out-coloring based on the iteration counter. */
 | 
					/* Compute the out-coloring based on the iteration counter. */
 | 
				
			||||||
pixel_t outcolor(int it) {
 | 
					pixel_t outcolor(int it) {
 | 
				
			||||||
  /* return 0x00010001 * ((it * 0xff) / max_it); */
 | 
					  return 0x00010001 * ((it * 0xff) / max_it);
 | 
				
			||||||
  return 0x00000001 * ((it * 0xff) / max_it);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* "Compute" the in-coloring. */
 | 
					/* "Compute" the in-coloring. */
 | 
				
			||||||
| 
						 | 
					@ -26,12 +28,10 @@ pixel_t incolor() {
 | 
				
			||||||
  return 0x00000000;            /* black */
 | 
					  return 0x00000000;            /* black */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Computes the visual representation of a mandelbrot set for a given line. */
 | 
				
			||||||
 | 
					void compmandelbrot_line(bitmap_t* mbrot, size_t y) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void drawmandelbrot(bitmap_t * mbrot) {
 | 
					  for (size_t x = 0; x < mbrot->width; x++) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < mbrot->width * mbrot->height; i++) {
 | 
					 | 
				
			||||||
    size_t y = i / mbrot->width;
 | 
					 | 
				
			||||||
    size_t x = i % mbrot->width;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    float complex c = ((3.0f * x / mbrot->width) - 2.0f)
 | 
					    float complex c = ((3.0f * x / mbrot->width) - 2.0f)
 | 
				
			||||||
        + I * ((2.0f * y / mbrot->height) - 1.0f);
 | 
					        + I * ((2.0f * y / mbrot->height) - 1.0f);
 | 
				
			||||||
| 
						 | 
					@ -57,14 +57,42 @@ void drawmandelbrot(bitmap_t * mbrot) {
 | 
				
			||||||
      color = incolor();
 | 
					      color = incolor();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t i = y * mbrot->width + x;
 | 
				
			||||||
    mbrot->pixels[i] = color;
 | 
					    mbrot->pixels[i] = color;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Next line to compute. */
 | 
				
			||||||
 | 
					size_t next_y = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Mutex for next_y. */
 | 
				
			||||||
 | 
					pthread_mutex_t next_y_mutex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Thread to compute mandelbrot. */
 | 
				
			||||||
 | 
					void *compmandelbrot(void *arg) {
 | 
				
			||||||
 | 
					  bitmap_t* mbrot = (bitmap_t*) arg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  size_t y = 0;
 | 
				
			||||||
 | 
					  while (y < mbrot->height) {
 | 
				
			||||||
 | 
					    int ret;
 | 
				
			||||||
 | 
					    ret = pthread_mutex_lock(&next_y_mutex);
 | 
				
			||||||
 | 
					    assert(ret == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    y = next_y++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = pthread_mutex_unlock(&next_y_mutex);
 | 
				
			||||||
 | 
					    assert(ret == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (y < mbrot->height) {
 | 
				
			||||||
 | 
					      compmandelbrot_line(mbrot, y);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pthread_exit(NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Write "bitmap" to a PNG file specified by "path"; returns 0 on
 | 
					/* Write "bitmap" to a PNG file specified by "path"; returns 0 on
 | 
				
			||||||
   success, non-zero on error. */
 | 
					   success, non-zero on error. */
 | 
				
			||||||
 | 
					 | 
				
			||||||
static int save_png_to_file(bitmap_t * bitmap, const char *path) {
 | 
					static int save_png_to_file(bitmap_t * bitmap, const char *path) {
 | 
				
			||||||
  FILE *fp;
 | 
					  FILE *fp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,8 +106,6 @@ static int save_png_to_file(bitmap_t * bitmap, const char *path) {
 | 
				
			||||||
   * is set to a value which means 'success'. */
 | 
					   * is set to a value which means 'success'. */
 | 
				
			||||||
  int status = -1;
 | 
					  int status = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* FIXME The following number is set by trial and error only. I cannot see
 | 
					 | 
				
			||||||
   * where it it is documented in the libpng manual.  */
 | 
					 | 
				
			||||||
  int pixel_size = 3;
 | 
					  int pixel_size = 3;
 | 
				
			||||||
  int depth = 8;
 | 
					  int depth = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,7 +147,6 @@ static int save_png_to_file(bitmap_t * bitmap, const char *path) {
 | 
				
			||||||
        png_malloc(png_ptr, sizeof(uint8_t) * bitmap->width * pixel_size);
 | 
					        png_malloc(png_ptr, sizeof(uint8_t) * bitmap->width * pixel_size);
 | 
				
			||||||
    row_pointers[y] = row;
 | 
					    row_pointers[y] = row;
 | 
				
			||||||
    for (x = 0; x < bitmap->width; ++x) {
 | 
					    for (x = 0; x < bitmap->width; ++x) {
 | 
				
			||||||
      /* FIXME */
 | 
					 | 
				
			||||||
      pixel_t *pixel = bitmap->pixels + (y*bitmap->width + x);
 | 
					      pixel_t *pixel = bitmap->pixels + (y*bitmap->width + x);
 | 
				
			||||||
      *row++ = (*pixel & 0x00ff0000) >> 16;
 | 
					      *row++ = (*pixel & 0x00ff0000) >> 16;
 | 
				
			||||||
      *row++ = (*pixel & 0x0000ff00) >> 8;
 | 
					      *row++ = (*pixel & 0x0000ff00) >> 8;
 | 
				
			||||||
| 
						 | 
					@ -144,7 +169,6 @@ static int save_png_to_file(bitmap_t * bitmap, const char *path) {
 | 
				
			||||||
  png_free(png_ptr, row_pointers);
 | 
					  png_free(png_ptr, row_pointers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
png_failure:
 | 
					png_failure:
 | 
				
			||||||
  /* FIXME info struct destroy? */
 | 
					 | 
				
			||||||
png_create_info_struct_failed:
 | 
					png_create_info_struct_failed:
 | 
				
			||||||
  png_destroy_write_struct(&png_ptr, &info_ptr);
 | 
					  png_destroy_write_struct(&png_ptr, &info_ptr);
 | 
				
			||||||
png_create_write_struct_failed:
 | 
					png_create_write_struct_failed:
 | 
				
			||||||
| 
						 | 
					@ -154,21 +178,54 @@ fopen_failed:
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char *argv[]) {
 | 
					int main(int argc, char *argv[]) {
 | 
				
			||||||
  bitmap_t mbrot;
 | 
					 | 
				
			||||||
  mbrot.width = 640;
 | 
					 | 
				
			||||||
  mbrot.height = 480; /* FIXME calculate */
 | 
					 | 
				
			||||||
  mbrot.pixels = calloc(sizeof(pixel_t), mbrot.width * mbrot.height);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Do the mandelbrot. */
 | 
					  /* Get command line options. */
 | 
				
			||||||
  drawmandelbrot(&mbrot);
 | 
					  int num_threads = 1;
 | 
				
			||||||
 | 
					  char* options = "j:";
 | 
				
			||||||
 | 
					  if (getopt(argc, argv, options) == 'j') {
 | 
				
			||||||
 | 
					    num_threads = (int) strtol(optarg, NULL, 10);
 | 
				
			||||||
 | 
					    assert(num_threads != 0);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    printf("Usage: multibrot -j <number_of_threads>\n");
 | 
				
			||||||
 | 
					    exit(0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Set up mandelbrot bitmap. */
 | 
				
			||||||
 | 
					  bitmap_t mbrot;
 | 
				
			||||||
 | 
					  mbrot.width = 1600;
 | 
				
			||||||
 | 
					  mbrot.height = 1200;
 | 
				
			||||||
 | 
					  mbrot.pixels = calloc(sizeof(pixel_t), mbrot.width * mbrot.height);
 | 
				
			||||||
 | 
					  assert(mbrot.pixels != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Start computing threads. */
 | 
				
			||||||
 | 
					  int ret;
 | 
				
			||||||
 | 
					  pthread_t* threads;
 | 
				
			||||||
 | 
					  threads = calloc(sizeof(pthread_t), num_threads);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ret = pthread_mutex_init(&next_y_mutex, NULL);
 | 
				
			||||||
 | 
					  assert(ret == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (int t = 0; t < num_threads; t++) {
 | 
				
			||||||
 | 
					    ret = pthread_create(&threads[t], NULL, compmandelbrot, (void *) &mbrot);
 | 
				
			||||||
 | 
					    assert(ret == 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Wait for the threads to finish. */
 | 
				
			||||||
 | 
					  for (int t = 0; t < num_threads; t++) {
 | 
				
			||||||
 | 
					    ret = pthread_join(threads[t], NULL);
 | 
				
			||||||
 | 
					    assert(ret == 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Save PNG */
 | 
					  /* Save PNG */
 | 
				
			||||||
  char *file = "multibrot.png";
 | 
					  char *file = "multibrot.png";
 | 
				
			||||||
  if (save_png_to_file(&mbrot, file) != 0) {
 | 
					  ret = save_png_to_file(&mbrot, file);
 | 
				
			||||||
    fprintf(stderr, "Could not save PNG to %s.\n", file);
 | 
					  assert(ret == 0);
 | 
				
			||||||
    exit(1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Quit. */
 | 
					  /* Quit. */
 | 
				
			||||||
 | 
					  pthread_exit(NULL);
 | 
				
			||||||
 | 
					  free(threads);
 | 
				
			||||||
 | 
					  free(mbrot.pixels);
 | 
				
			||||||
 | 
					  ret = pthread_mutex_destroy(&next_y_mutex);
 | 
				
			||||||
 | 
					  assert(ret == 0);
 | 
				
			||||||
  return 0;
 | 
					  return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue