From ee2b6b950679034a55dd03907c90c23c9ed4d329 Mon Sep 17 00:00:00 2001 From: go38coz Date: Fri, 26 Jun 2026 19:31:25 +0200 Subject: [PATCH 1/4] Copied tricorn to tricorn_simd .h and .c files to be later modified into SIMD optimization. Changed converge definition to static in order to not have conflicts with other versions of the function as it is an internal helper function --- Implementierung/tricorn.c | 4 +-- Implementierung/tricorn_simd.c | 51 ++++++++++++++++++++++++++++++++++ Implementierung/tricorn_simd.h | 5 ++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 Implementierung/tricorn_simd.c create mode 100644 Implementierung/tricorn_simd.h diff --git a/Implementierung/tricorn.c b/Implementierung/tricorn.c index dec0b17..7c54c14 100644 --- a/Implementierung/tricorn.c +++ b/Implementierung/tricorn.c @@ -1,7 +1,7 @@ #include "tricorn.h" static const float convergenceLimit = 2.0f; // Mandelbrot set limit -unsigned converge(float complex position,unsigned n); +static unsigned converge(float complex position,unsigned n); void tricorn(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img){ @@ -34,7 +34,7 @@ void tricorn(float complex start, size_t width, ssize_t height, float res, unsig } // Iterative application of the equation given by the assignment -unsigned converge(float complex position,unsigned n){ +static unsigned converge(float complex position,unsigned n){ complex float result = 0; for(unsigned i = 0; i < n; i++){ diff --git a/Implementierung/tricorn_simd.c b/Implementierung/tricorn_simd.c new file mode 100644 index 0000000..e40f95f --- /dev/null +++ b/Implementierung/tricorn_simd.c @@ -0,0 +1,51 @@ +#include "tricorn.h" + +static const float convergenceLimit = 2.0f; // Mandelbrot set limit +static unsigned converge(float complex position,unsigned n); + +void tricorn_simd(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img){ + + // Absolute height for iteration purposes + height = height < 0 ? height*-1 : height; + + // Simple iteration over every pixel in the picture + for(size_t i = 0; i < width; i++){ + for(ssize_t j = 0; j < height; j++){ + + unsigned value = converge(start + (i*res - I*j*res),n); + + // Pixel value position in the output array + size_t position = (j*width +i) *3; + + + if(!color){ // Grayscale colors as specified by the assignment r = g = b + img[position] = value; + img[position + 1] = value; + img[position + 2] = value; + } + else{ // Temporary colors with random multipliers + img[position] = ((value*3) % 255); + img[position + 1] = ((value*2) % 255) ; + img[position + 2] = ((value*5) % 255); + } + + } + } +} + +// Iterative application of the equation given by the assignment +static unsigned converge(float complex position,unsigned n){ + + complex float result = 0; + for(unsigned i = 0; i < n; i++){ + // Application of the given complex equation with m = 2 + result = cpowf(conjf(result),2) + position; + + // Detection of unbounded behaviour + // If the magnitude of the result vector is greater than the convergence limit, we know that the function will grow unbounded + // Current convergence limit taken from mandelbrot set + if(sqrtf(powf (crealf(result),2)+powf(cimagf(result),2)) > convergenceLimit) + return 255-4*(i % 64); + } + return 0; +} \ No newline at end of file diff --git a/Implementierung/tricorn_simd.h b/Implementierung/tricorn_simd.h new file mode 100644 index 0000000..60b9618 --- /dev/null +++ b/Implementierung/tricorn_simd.h @@ -0,0 +1,5 @@ +#include +#include +#include +#include +void tricorn_simd(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img); \ No newline at end of file From 791231a4f5704ba998348aeacc05ca70710cd914 Mon Sep 17 00:00:00 2001 From: go38coz Date: Fri, 26 Jun 2026 22:38:55 +0200 Subject: [PATCH 2/4] Implemented SIMD optimizations in tricorn_simd including negative height handling which needs to be fixed in normal tricorn.c --- Implementierung/Makefile | 2 +- Implementierung/main.c | 13 ++-- Implementierung/tricorn_simd.c | 122 +++++++++++++++++++++++++-------- 3 files changed, 102 insertions(+), 35 deletions(-) diff --git a/Implementierung/Makefile b/Implementierung/Makefile index 321ec74..f74f995 100644 --- a/Implementierung/Makefile +++ b/Implementierung/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -Wall -Wextra -O3 -std=c17 +CFLAGS = -Wall -Wextra -O3 -std=c17 -msse4.1 DEPFLAGS = -MMD -MP LDFLAGS = -lm diff --git a/Implementierung/main.c b/Implementierung/main.c index f9a848e..05fb56e 100644 --- a/Implementierung/main.c +++ b/Implementierung/main.c @@ -6,6 +6,7 @@ #include // for getopt_long #include // for strcpy #include "tricorn.h" +#include "tricorn_simd.h" void save_bmp(const char *filename, unsigned char *img, size_t width, ssize_t height,_Bool color); @@ -17,7 +18,7 @@ unsigned tests(void) { void benchmarking(void) { // do the benchmarking } -// REMOVE LATER: interesting test command ./tricorn -s -2.0,0.6 -r 0.0010 -n 200 -d 1920 1080 -o image.bmp -C +// REMOVE LATER: interesting test command ./tricorn -s -2.0,0.6 -r 0.0010 -n 200 -d 1920,-1080 -o image.bmp -C const char* help_messsage = "Help Message\n\n" "Usage:\n" " ./tricorn [OPTIONS]\n\n" @@ -200,7 +201,7 @@ int main(int argc, char** argv) unsigned char tempArr[width*height*bytesPerColour]; // if not colored, rem printf("yashar\n"); - tricorn(complexA,width,height,resolution,n,colored,tempArr); + tricorn_simd(complexA,width,height,resolution,n,colored,tempArr); printf("yashar\n"); save_bmp(output_data,tempArr,width,height,colored); @@ -218,14 +219,16 @@ void save_bmp(const char *filename, unsigned char *img, size_t width, ssize_t he exit(EXIT_FAILURE); } + size_t bytesPerColor = color ? 3 : 1; + + int actual_height = (height < 0) ? -height : height; // Calculate absolute height for iteration and calculation purposes - size_t row_padding = (4 - (width) * 3 % 4) % 4; // Calculate padding for each row as .bmp is 4 bytes aligned - size_t row_width = width * 3 + row_padding; // Calculate row width with padding + size_t row_padding = (4 - (width * bytesPerColor) % 4) % 4; // Calculate padding for each row as .bmp is 4 bytes aligned + size_t row_width = width * bytesPerColor + row_padding; // Calculate row width with padding size_t image_size = actual_height * row_width; size_t file_size = image_size + 54; // File size = Image Size + Headers - size_t bytesPerColor = color ? 3 : 1; // File header - contains information about the file unsigned char file_header[14] = { diff --git a/Implementierung/tricorn_simd.c b/Implementierung/tricorn_simd.c index e40f95f..fc4accc 100644 --- a/Implementierung/tricorn_simd.c +++ b/Implementierung/tricorn_simd.c @@ -1,51 +1,115 @@ -#include "tricorn.h" +#include "tricorn_simd.h" +#include -static const float convergenceLimit = 2.0f; // Mandelbrot set limit -static unsigned converge(float complex position,unsigned n); +static unsigned converge(float c_rea, float c_imag, unsigned n); void tricorn_simd(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img){ + int bytesPerColor = color ? 3 : 1; // Absolute height for iteration purposes - height = height < 0 ? height*-1 : height; + ssize_t actual_height = height < 0 ? (ssize_t)(-height) : height; + ssize_t imag_step = height < 0 ? -1:1; + size_t simd_width = (width / 4) * 4; - // Simple iteration over every pixel in the picture - for(size_t i = 0; i < width; i++){ - for(ssize_t j = 0; j < height; j++){ + float c_start_real = creal(start); + float c_start_imag = cimag(start); - unsigned value = converge(start + (i*res - I*j*res),n); + __m128 four = _mm_set1_ps(4.0f); + __m128 minus_two = _mm_set1_ps(-2.0f); - // Pixel value position in the output array - size_t position = (j*width +i) *3; + for(ssize_t y = 0; y < actual_height;y++) { + float c_imag = c_start_imag - (y*imag_step) * res; + __m128 c_imag_vector = _mm_set1_ps(c_imag); + size_t x = 0; + for(; x < simd_width;x += 4) { //Simd + float c_real = c_start_real + (x*res); + __m128 c_real_vector = _mm_setr_ps(c_real,c_real+1.0f*res,c_real+2.0f*res,c_real+3.0f*res); - if(!color){ // Grayscale colors as specified by the assignment r = g = b - img[position] = value; - img[position + 1] = value; - img[position + 2] = value; + __m128 real = c_real_vector; + __m128 imag = c_imag_vector; + __m128i iterations = _mm_setzero_si128(); + __m128i active_mask = _mm_set1_epi32(-1); + + for(unsigned i = 0; i < n;i++) { + __m128 real2 = _mm_mul_ps(real,real); + __m128 imag2 = _mm_mul_ps(imag,imag); + + __m128 unbounded = _mm_cmplt_ps(_mm_add_ps(real2,imag2),four); + __m128i unbounded_i = _mm_castps_si128(unbounded); + + active_mask = _mm_and_si128(active_mask,unbounded_i); + + if(_mm_test_all_zeros(active_mask,active_mask)) { + break; + } + + iterations = _mm_sub_epi32(iterations,active_mask); + + imag = _mm_add_ps(c_imag_vector,_mm_mul_ps(minus_two, _mm_mul_ps(real,imag))); + real = _mm_add_ps(_mm_sub_ps(real2,imag2),c_real_vector); } - else{ // Temporary colors with random multipliers + + + __m128i mod64 = _mm_and_si128(iterations, _mm_set1_epi32(63)); + __m128i times4 = _mm_slli_epi32(mod64, 2); + __m128i values = _mm_sub_epi32(_mm_set1_epi32(255), times4); + + __m128i max_iter = _mm_set1_epi32(n); + __m128i is_max = _mm_cmpeq_epi32(iterations, max_iter); + values = _mm_andnot_si128(is_max, values); + + unsigned int value_array[4]; + _mm_storeu_si128((__m128i*)value_array,values); + + for(unsigned i = 0 ; i < 4;i++) { + unsigned int value = value_array[i]; + size_t position = (y*width + (x+i)) * bytesPerColor; + + if(!color) { + img[position] = value; + } + else { + img[position] = ((value*3) % 255); + img[position + 1] = ((value*2) % 255); + img[position + 2] = ((value*5) % 255); + } + } + } + + for(;x < width; x++) { + float c_real = c_start_real + (x*res); + unsigned int value = converge(c_real,c_imag,n); + + size_t position = (y*width + x) * bytesPerColor; + + if(!color) { + img[position] = value; + } + else { img[position] = ((value*3) % 255); - img[position + 1] = ((value*2) % 255) ; + img[position + 1] = ((value*2) % 255); img[position + 2] = ((value*5) % 255); } - } + } } -// Iterative application of the equation given by the assignment -static unsigned converge(float complex position,unsigned n){ +static unsigned converge(float c_real, float c_imag, unsigned n) { + float real = 0.0f; + float imag = 0.0f; - complex float result = 0; - for(unsigned i = 0; i < n; i++){ - // Application of the given complex equation with m = 2 - result = cpowf(conjf(result),2) + position; + for (unsigned i = 0; i < n; i++) { + float real2 = real * real; + float imag2 = imag * imag; - // Detection of unbounded behaviour - // If the magnitude of the result vector is greater than the convergence limit, we know that the function will grow unbounded - // Current convergence limit taken from mandelbrot set - if(sqrtf(powf (crealf(result),2)+powf(cimagf(result),2)) > convergenceLimit) - return 255-4*(i % 64); + if (real2 + imag2 > 4.0f) { + return 255 - 4 * (i % 64); + } + + imag = -2.0f * real * imag + c_imag; + real = real2 - imag2 + c_real; } - return 0; + return 0; } \ No newline at end of file From 9e9687685247a5dec19923e67ce91d6d5922bd34 Mon Sep 17 00:00:00 2001 From: go38coz Date: Fri, 26 Jun 2026 23:07:45 +0200 Subject: [PATCH 3/4] main function now allocates image array on the heap to be able to support larger images --- Implementierung/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Implementierung/main.c b/Implementierung/main.c index 05fb56e..94d06d7 100644 --- a/Implementierung/main.c +++ b/Implementierung/main.c @@ -199,13 +199,18 @@ int main(int argc, char** argv) bytesPerColour = 1; } - unsigned char tempArr[width*height*bytesPerColour]; // if not colored, rem + unsigned char *tempArr = malloc(width*abs(height)*bytesPerColour);// allocate image array on the heap to support large images as the stack is usually only 8 MB big + if(tempArr == NULL) { + exit(EXIT_FAILURE); + } + printf("yashar\n"); tricorn_simd(complexA,width,height,resolution,n,colored,tempArr); printf("yashar\n"); save_bmp(output_data,tempArr,width,height,colored); - free(output_data); // + free(output_data); + free(tempArr); return 0; } From a3d829533cabb564606e98e58f8dc1fe4575b7d1 Mon Sep 17 00:00:00 2001 From: go38coz Date: Sun, 28 Jun 2026 20:49:00 +0200 Subject: [PATCH 4/4] Added implementation switching based on -V parameter --- Implementierung/main.c | 10 ++++++++-- Implementierung/tricorn_simd.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Implementierung/main.c b/Implementierung/main.c index 94d06d7..46ae555 100644 --- a/Implementierung/main.c +++ b/Implementierung/main.c @@ -199,13 +199,19 @@ int main(int argc, char** argv) bytesPerColour = 1; } - unsigned char *tempArr = malloc(width*abs(height)*bytesPerColour);// allocate image array on the heap to support large images as the stack is usually only 8 MB big + ssize_t absolute_height = height >= 0 ? height : height * - 1; + unsigned char *tempArr = malloc(width*absolute_height*bytesPerColour);// allocate image array on the heap to support large images as the stack is usually only 8 MB big if(tempArr == NULL) { exit(EXIT_FAILURE); } printf("yashar\n"); - tricorn_simd(complexA,width,height,resolution,n,colored,tempArr); + if(version_to_use == 0) { + tricorn_simd(complexA,width,height,resolution,n,colored,tempArr); + } + else { + tricorn(complexA,width,height,resolution,n,colored,tempArr); + } printf("yashar\n"); save_bmp(output_data,tempArr,width,height,colored); diff --git a/Implementierung/tricorn_simd.c b/Implementierung/tricorn_simd.c index fc4accc..de6cea7 100644 --- a/Implementierung/tricorn_simd.c +++ b/Implementierung/tricorn_simd.c @@ -22,7 +22,7 @@ void tricorn_simd(float complex start, size_t width, ssize_t height, float res, __m128 c_imag_vector = _mm_set1_ps(c_imag); size_t x = 0; - for(; x < simd_width;x += 4) { //Simd + for(; x < simd_width;x += 4) { //SIMD Loop float c_real = c_start_real + (x*res); __m128 c_real_vector = _mm_setr_ps(c_real,c_real+1.0f*res,c_real+2.0f*res,c_real+3.0f*res); @@ -35,7 +35,7 @@ void tricorn_simd(float complex start, size_t width, ssize_t height, float res, __m128 real2 = _mm_mul_ps(real,real); __m128 imag2 = _mm_mul_ps(imag,imag); - __m128 unbounded = _mm_cmplt_ps(_mm_add_ps(real2,imag2),four); + __m128 unbounded = _mm_cmple_ps(_mm_add_ps(real2,imag2),four); __m128i unbounded_i = _mm_castps_si128(unbounded); active_mask = _mm_and_si128(active_mask,unbounded_i);