From a29f32a303177ddb25805f746179c216166f8469 Mon Sep 17 00:00:00 2001 From: Anton Lydike Date: Mon, 16 Mar 2020 09:49:47 +0100 Subject: [PATCH] multithreaded mandelbrot fun --- .gitignore | 3 +- images/bmp-format.c | 76 ++++++++++++++++++----------------- images/images.c | 7 ++++ main.c | 97 ++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 137 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index 82f4fc1..6af6055 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.out *.o -*.bmp \ No newline at end of file +*.png +*.bmp diff --git a/images/bmp-format.c b/images/bmp-format.c index f49ce6d..fd1f9ee 100644 --- a/images/bmp-format.c +++ b/images/bmp-format.c @@ -1,32 +1,33 @@ #include #include +#include #include "images.h" // write an int to a byte array -void bmp__write_int(unsigned char* bytes, int num) { +void bmp__write_int(FILE *fp, int num) { for (int i = 0; i < 4; i++) { - bytes[i] = num >> (i * 8); + putc(num >> (i * 8), fp); } } -void bmp__write_16bit(unsigned char* bytes, int num) { +void bmp__write_16bit(FILE *fp, int num) { for (int i = 0; i < 2; i++) { - bytes[i] = num >> (i * 8); + putc(num >> (i * 8), fp); } } -int bmp__align_to_four_bytes(int row_length) { - int os =row_length % 4; +unsigned long int bmp__align_to_four_bytes(unsigned long int row_length) { + unsigned long int os =row_length % 4; return os == 0 ?row_length : row_length + (4 - os); } -int bmp__image_byte_size(Image image) { +unsigned long int bmp__image_byte_size(Image image) { // calculate size: // bitmap file header = 14 // BITMAPCOREHEADER = 12 bytes - int size = 14 + 12; + unsigned long int size = 14 + 12; // width must be padded to align to 4 bytes - int row_bytes = bmp__align_to_four_bytes(image.width * 3); // 3 bytes per pixel + unsigned long int row_bytes = bmp__align_to_four_bytes(image.width * 3); // 3 bytes per pixel // add row_bytes * height to total size size += row_bytes * image.height; @@ -53,43 +54,46 @@ int bmp__image_byte_size(Image image) { // bytes[2] => wert an offset 2 (ist char) int image_save_bmp(Image image, const char* path) { - int size = bmp__image_byte_size(image); - unsigned char* bytes = calloc(sizeof(char), size); + unsigned long int size = bmp__image_byte_size(image); + if ((unsigned long int) size > (unsigned long int) UINT_MAX) { + printf("bitmap is too big!\n"); + return 0; + } else { + printf("predicted size is %lu out of %u possible", size, UINT_MAX); + } + FILE *fp = fopen(path, "w"); + //unsigned char* bytes = calloc(sizeof(char), size); // magic bytes, signature "BM" // bmp header: - bytes[0] = 'B'; - bytes[1] = 'M'; + fputs("BM", fp); // offset 0 // write size to header - bmp__write_int(bytes + 2, size); - // the next 4 byte are zeros + bmp__write_int(fp, size); // offset 2 + bmp__write_int(fp, 0); // offset 6 - the next 4 byte are zeros // pixel array offset - bmp__write_int(bytes + 10, 14 + 12); + bmp__write_int(fp, 14 + 12); // offset 10 // DIB header: - bmp__write_int(bytes + 14, 12); // header size - bmp__write_16bit(bytes + 18, image.width); // width - bmp__write_16bit(bytes + 20, image.height); // height - bmp__write_16bit(bytes + 22, 1); // color planes - bmp__write_16bit(bytes + 24, 3 * 8); // bits per pixel + bmp__write_int(fp, 12); // header size + bmp__write_16bit(fp, image.width); // width + bmp__write_16bit(fp, image.height); // height + bmp__write_16bit(fp, 1); // color planes + bmp__write_16bit(fp, 3 * 8); // bits per pixel - int byte_offset = 26; - int byte_pos = 0; - int line; + int line, px; for (line = 0; line < image.height; line++) { - byte_pos = 0; - for (int px = 0; px < image.width * 3; px += 3) { - bytes[byte_offset + byte_pos++] = image.bitmap[line][px + 2]; - bytes[byte_offset + byte_pos++] = image.bitmap[line][px + 1]; - bytes[byte_offset + byte_pos++] = image.bitmap[line][px + 0]; + for (px = 0; px < image.width * 3; px += 3) { + putc(image.bitmap[line][px + 2], fp); + putc(image.bitmap[line][px + 1], fp); + putc(image.bitmap[line][px + 0], fp); + } + int mod = px % 4; + if (mod != 0) { + for (int i = 0; i < 4-mod; i++) { + putc(0, fp); + } } - byte_offset += bmp__align_to_four_bytes(byte_pos); } - printf("generated %i bytes out of %i calculated ones\n", byte_offset, size); - - FILE *fp = fopen(path, "w"); - int written = fwrite(bytes, sizeof(char), size, fp); - printf("wrote %i bytes...\n", written); + printf("wrote %li bytes...\n", size); fclose(fp); - free(bytes); return 1; } diff --git a/images/images.c b/images/images.c index 8da97c5..ecf8261 100644 --- a/images/images.c +++ b/images/images.c @@ -1,5 +1,6 @@ #include #include +#include #include "images.h" @@ -11,6 +12,12 @@ int image_new(int width, int height, Image* img) { img->height = height; img->width = width; + // approximation + if ((unsigned long int) width * height * 3 > (unsigned long int) UINT_MAX) { + printf("Image dimensions to large for a bitmap!\n"); + return 0; + } + // initialize bitmap... img->bitmap = malloc(height * sizeof(char*)); for (int i = 0; i < height; i++) { diff --git a/main.c b/main.c index efeae0f..d08acd7 100644 --- a/main.c +++ b/main.c @@ -1,12 +1,22 @@ -#include #include -#include +#include +#include +#include +#include + #include "images/images.h" #include "complex.c" + +#define MB_THREADS 7 + void draw_mandelbrot(int width, int height, int iterations); void draw_mandelbrot_auto(int width, int iterations); int test_mandelbrot(Complex c, int iterations); +void* create_shared_memory(size_t size); +int image_new_shared(int width, int height, Image* img); +void image_destroy_shared(Image img); + int main(int argc, char* argv[]) { int width = 3000; @@ -22,7 +32,7 @@ int main(int argc, char* argv[]) { } void draw_mandelbrot_auto(int width, int iterations) { - draw_mandelbrot(width, 2 * width / 3, iterations); + draw_mandelbrot(width, 2.5f * width / 3, iterations); } void draw_mandelbrot(int width, int height, int iterations) { @@ -31,24 +41,51 @@ void draw_mandelbrot(int width, int height, int iterations) { float step = 3.0f / width; Image img; - image_new(width, height, &img); + image_new_shared(width, height, &img); + + int thread_id = 0; + + for (int i = 0; i < MB_THREADS - 1; i++) { + if (fork() == 0) { + thread_id = i + 1; + break; + } + } + printf("Thread %i reporting for duty\n", thread_id); for (int x = 0; x < width; x++) { + if (x % MB_THREADS != thread_id) continue; + for (int y = 0; y < height; y++) { Complex c = complex_new( (x - center_x) * step, (y - center_y) * step ); int ret = test_mandelbrot(c, iterations); - int r = (ret * 160 / iterations); - int g = (ret * 9 / iterations); - int b = (ret * 165 / iterations); - image_set_px(img, x, y, r,g,b); + //int r = (ret * 160 / iterations); + //int g = (ret * 9 / iterations); + //int b = (ret * 165 / iterations); + //image_set_px(img, x, y, r,g,b); + int color = 255 * ret / (float) iterations; + image_set_px(img, x, y, color, color, color); } } + printf("Thread %i is finished\n", thread_id); + if (thread_id != 0) { + exit(0); + } + + int status; + for (int i = 0; i < MB_THREADS - 1; i++) { + printf("Waiting for threads... %d/%d\n", i, MB_THREADS - 1); + while(wait(&status) > 0) {} + } + + printf("Got %d/%d threads\n", MB_THREADS - 1, MB_THREADS - 1); + image_save_bmp(img, "mandelbrot.bmp"); - image_destroy(img); + image_destroy_shared(img); } int test_mandelbrot(Complex c, int iterations) { @@ -61,3 +98,45 @@ int test_mandelbrot(Complex c, int iterations) { return 0; } + + +int image_new_shared(int width, int height, Image* img) { + if (img == NULL) return 0; + + img->height = height; + img->width = width; + + if ((unsigned long int) width * height * 3 > (unsigned long int) UINT_MAX) { + printf("Image dimensions to large for a bitmap!\n"); + return 0; + } + + // initialize bitmap... + img->bitmap = create_shared_memory(height * sizeof(char*)); + for (int i = 0; i < height; i++) { + img->bitmap[i] = create_shared_memory(3 * width * sizeof(char)); + } + + return 1; +} + +void image_destroy_shared(Image img) { + for (int i = 0; i < img.height; i++) { + munmap(img.bitmap[i], 3 * img.width * sizeof(char)); + } + munmap(img.bitmap, img.height * sizeof(char*)); +} + +void* create_shared_memory(size_t size) { + // Our memory buffer will be readable and writable: + int protection = PROT_READ | PROT_WRITE; + + // The buffer will be shared (meaning other processes can access it), but + // anonymous (meaning third-party processes cannot obtain an address for it), + // so only this process and its children will be able to use it: + int visibility = MAP_SHARED | MAP_ANONYMOUS; + + // The remaining parameters to `mmap()` are not important for this use case, + // but the manpage for `mmap` explains their purpose. + return mmap(NULL, size, protection, visibility, -1, 0); +} \ No newline at end of file