diff --git a/Implementierung/main.c b/Implementierung/main.c index 5aa96dd..206cfe6 100644 --- a/Implementierung/main.c +++ b/Implementierung/main.c @@ -14,8 +14,7 @@ #include "tricorn_V1.h" #include "tricorn_V2.h" #include "tricorn_V3.h" -#include "tricorn_V4.h" -#include "tricorn_V5.h" + void save_bmp(const char *filename, unsigned char *img, size_t width, ssize_t height,_Bool color); @@ -28,29 +27,21 @@ void benchmarking(size_t benchmarking_amount, int version_to_use, float complex switch (version_to_use) { case 0: // Uses highly optimized SIMD and threaded version / main implementation - printf("Benchmarking V0 - highly optimized SIMD + Threading implementation\n"); + printf("Benchmarking V0 - highly optimized SIMD\n"); tricorn_implementation = tricorn; break; case 1: // Uses unoptimised scalar comparison implementation - printf("Benchmarking V1 - unoptimized comparison implementation\n"); + printf("Benchmarking V1 - scalar comparison implementation\n"); tricorn_implementation = tricorn_V1; break; case 2: // Uses optimized SISD implementation - printf("Benchmarking V2 - optimized SISD implementation\n"); + printf("Benchmarking V2 - unoptimized SIMD implementation\n"); tricorn_implementation = tricorn_V2; break; case 3: // Uses unoptimized SIMD implementation - printf("Benchmarking V3 - unoptimized SIMD implementation\n"); + printf("Benchmarking V3 - highly optimized SIMD + Threading implementation\n"); tricorn_implementation = tricorn_V3; break; - case 4: // Uses highly optimized SIMD - printf("Benchmarking V4 - optimized SIMD implementation\n"); - tricorn_implementation = tricorn_V4; - break; - case 5: // Experimental cleanups and inlines / for testing - printf("Benchmarking V5 - experimental implementation\n"); - tricorn_implementation = tricorn_V5; - break; default: fprintf(stderr,"Invalid version to use: V%d. Available versions are : V0,V1,V2,V3,V4,V5\n",version_to_use); return; @@ -433,29 +424,21 @@ int main(int argc, char** argv) switch (version_to_use) { case 0: - // Uses highly optimized SIMD and threaded version / main implementation + // Uses highly optimized SIMD - main implementation tricorn(complexA,width,height,resolution,n,colored,tempArr); break; case 1: - // Uses unoptimised comparison implementation + // Uses scalar comparison implementation tricorn_V1(complexA,width,height,resolution,n,colored,tempArr); break; case 2: - // Uses optimized SISD version + // Uses unoptimized SIMD implementation tricorn_V2(complexA,width,height,resolution,n,colored,tempArr); break; case 3: - // Uses unoptimized SIMD version + // Uses highly optimized SIMD + Threading implementation tricorn_V3(complexA,width,height,resolution,n,colored,tempArr); break; - case 4: - // Uses highly optimized SIMD - tricorn_V4(complexA,width,height,resolution,n,colored,tempArr); - break; - case 5: - // Experimental cleanups and inlines / for testing - tricorn_V5(complexA,width,height,resolution,n,colored,tempArr); - break; default: break; } diff --git a/Implementierung/tricorn.c b/Implementierung/tricorn.c index 6546027..e66d6fb 100644 --- a/Implementierung/tricorn.c +++ b/Implementierung/tricorn.c @@ -1,29 +1,11 @@ #include "tricorn.h" +#include -static void* compute(void * args); static unsigned converge(float c_real, float c_imag, unsigned n); -static size_t get_thread_count(); -// Argument structs passed to the threads for the caluclations -typedef struct{ - unsigned n; - _Bool color; - _Bool flipped; - float res; - size_t width; - unsigned char* img; - -} const_args; -typedef struct { - float start_r; - float start_i; - size_t start_row; - size_t stop_row; - const_args* consts; -} compute_args; /** - * @brief Generates a Tricorn fractal image using an optimized SIMD + Threading implementation. + * @brief Generates a Tricorn fractal image using an optimized SIMD algorithm * @param start The starting complex coordinate (bottom-left or top-left depending on height sign). * @param width The width of the target image in pixels. * @param height The height of the target image. A negative height reverses the Y-axis calculation. @@ -33,88 +15,39 @@ typedef struct { * @param img Pointer to the pre-allocated memory buffer where the bitmap data will be stored. */ void tricorn(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img){ - - const ssize_t min_rows_per_thread = 10; - const _Bool flipped = height < 0; - const size_t abs_height = flipped ? -height : height; - - - // Calculating the threadcount so that it is limited to a reasonable distribution where thread starting doesnt outway calculation cost - size_t thread_count = get_thread_count(); - if(abs_height >= min_rows_per_thread) - while((abs_height/thread_count) < min_rows_per_thread) thread_count--; - else thread_count = 1; - - - - const size_t y_step = (abs_height+thread_count-1)/thread_count; - const float start_r = crealf(start); - const float directional_res = (flipped ? -1.0f: 1.0f)*res; - const float start_i = height > 0 ? cimag(start) : cimag(start) + ((abs_height - 1) * res); - - const_args* consts = malloc(sizeof(*consts)); - consts->width = width; - consts->flipped = flipped; - consts->color = color; - consts->res = res; - consts->img = img; - consts->n = n; - - pthread_t threads[thread_count]; - for(size_t i = 0; i < thread_count; i++){ - compute_args* args = malloc(sizeof(*args)); - args->start_r =start_r; - args->start_i =start_i+i*directional_res*y_step; - args->start_row = i*y_step; - args->stop_row = abs_height < (args->start_row+y_step) ? abs_height: (args->start_row+y_step); - args->consts = consts; - if(pthread_create(&threads[i],NULL,compute,args)){ - compute(args); - free(args); - args = NULL; - } - } - for(size_t i = 0; i < thread_count; i++){ - pthread_join(threads[i],NULL); - } - - free(consts); - consts = NULL; - return; -} - -static void* compute(void * vargs){ - - compute_args *args = vargs; - const_args *c = args->consts; - vargs = NULL; - - - const int bytes_per_color = c->color ? 3 : 1; + int bytesPerColor = color ? 3 : 1; + // Absolute height for iteration purposes - const size_t simd_width = (c->width / 4) * 4; + ssize_t actual_height = height < 0 ? (ssize_t)(-height) : height; + size_t simd_width = (width / 4) * 4; + + // Depending on the sign of height we generate the image top-down or bottom up + // As we iterate the image buffer from the top, if height is negative we would have to start at the end of the imaginary part and go in the oposite direction when generating + ssize_t imag_direction = height < 0 ? -1 : 1; + float c_start_real = creal(start); + float c_start_imag = height > 0 ? cimag(start) : cimag(start) + (actual_height - 1) * res; - float c_start_real = args->start_r; __m128 four = _mm_set1_ps(4.0f); __m128 minus_two = _mm_set1_ps(-2.0f); - float c_imag = args->start_i; + float c_imag = c_start_imag; __m128 c_imag_vector = _mm_set1_ps(c_imag); - float four_res = 4 * c->res; - float vertical_step = (c->flipped? -1.0f: 1.0f) * c->res; + float four_res = 4 * res; + + float vertical_step = imag_direction * res; __m128 vertical_step_vector = _mm_set1_ps(vertical_step); - for(size_t y = args->start_row; y < args->stop_row;y++) { + for(ssize_t y = 0; y < actual_height;y++) { size_t x = 0; - __m128 c_real_vector = _mm_setr_ps(c_start_real,c_start_real + 1.0f*c->res,c_start_real + 2.0f*c->res, c_start_real + 3.0f*c->res); + __m128 c_real_vector = _mm_setr_ps(c_start_real,c_start_real + 1.0f*res,c_start_real + 2.0f*res, c_start_real + 3.0f*res); for(; x < simd_width;x += 4) { //SIMD Loop __m128 real = _mm_setzero_ps(); __m128 imag = _mm_setzero_ps(); __m128i iterations = _mm_setzero_si128(); __m128i active_mask = _mm_set1_epi32(-1); - for(unsigned i = 0; i < c->n;i++) { + for(unsigned i = 0; i < n;i++) { __m128 real2 = _mm_mul_ps(real,real); __m128 imag2 = _mm_mul_ps(imag,imag); @@ -138,7 +71,7 @@ static void* compute(void * vargs){ __m128i times4 = _mm_slli_epi32(mod64, 2); __m128i values = _mm_sub_epi32(_mm_set1_epi32(255), times4); - __m128i max_iter = _mm_set1_epi32(c->n); + __m128i max_iter = _mm_set1_epi32(n); __m128i is_max = _mm_cmpeq_epi32(iterations, max_iter); values = _mm_andnot_si128(is_max, values); @@ -147,44 +80,40 @@ static void* compute(void * vargs){ for(unsigned i = 0 ; i < 4;i++) { unsigned int value = value_array[i]; - size_t position = (y*c->width + (x+i)) * bytes_per_color; + size_t position = (y*width + (x+i)) * bytesPerColor; - if(!c->color) { - c->img[position] = value; + if(!color) { + img[position] = value; } else { - c->img[position] = ((value*3) % 255); - c->img[position + 1] = ((value*2) % 255); - c->img[position + 2] = ((value*5) % 255); + img[position] = ((value*3) % 255); + img[position + 1] = ((value*2) % 255); + img[position + 2] = ((value*5) % 255); } } c_real_vector = _mm_add_ps(c_real_vector,_mm_set1_ps(four_res)); } - for(;x < c->width; x++) { - float c_real = c_start_real + (x*c->res); - unsigned int value = converge(c_real,c_imag,c->n); + 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*c->width + x) * bytes_per_color; + size_t position = (y*width + x) * bytesPerColor; - if(!c->color) { - c->img[position] = value; + if(!color) { + img[position] = value; } else { - c->img[position] = ((value*3) % 255); - c->img[position + 1] = ((value*2) % 255); - c->img[position + 2] = ((value*5) % 255); + img[position] = ((value*3) % 255); + img[position + 1] = ((value*2) % 255); + img[position + 2] = ((value*5) % 255); } } c_imag += vertical_step; c_imag_vector = _mm_add_ps(c_imag_vector,vertical_step_vector); } - free(args); - args = NULL; - return NULL; } -// Iterative application of the equation given by the assignment static unsigned converge(float c_real, float c_imag, unsigned n) { float real = 0.0f; float imag = 0.0f; @@ -201,13 +130,4 @@ static unsigned converge(float c_real, float c_imag, unsigned n) { real = real2 - imag2 + c_real; } return 0; -} -static inline size_t get_thread_count(){ - #ifdef WIN32 - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return sysinfo.dwNumberOfProcessors; - #else - return sysconf(_SC_NPROCESSORS_ONLN); - #endif } \ No newline at end of file diff --git a/Implementierung/tricorn.h b/Implementierung/tricorn.h index 165f93c..f5b4935 100644 --- a/Implementierung/tricorn.h +++ b/Implementierung/tricorn.h @@ -2,12 +2,6 @@ #include #include #include +#include #include -#include -#ifdef WIN32 -#include -#else -#include - -#endif -void tricorn(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img); \ No newline at end of file +void tricorn(float complex start, size_t width, ssize_t height, float res, unsigned n, bool color, unsigned char* img); \ No newline at end of file diff --git a/Implementierung/tricorn_V1.c b/Implementierung/tricorn_V1.c new file mode 100644 index 0000000..8f61100 --- /dev/null +++ b/Implementierung/tricorn_V1.c @@ -0,0 +1,77 @@ +#include "tricorn_V1.h" + +static unsigned converge(float c_real, float c_imag, unsigned n); + + +/** + * @brief Generates a Tricorn fractal image using an optimized scalar implementation. + * @param start The starting complex coordinate (bottom-left or top-left depending on height sign). + * @param width The width of the target image in pixels. + * @param height The height of the target image. A negative height reverses the Y-axis calculation. + * @param res The step size (resolution) per pixel in the complex plane. + * @param n The maximum number of mathematical iterations per pixel. + * @param color If true, generates a 24-bit RGB image. If false, writes a grayscale image. + * @param img Pointer to the pre-allocated memory buffer where the bitmap data will be stored. + */ +void tricorn_V1(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 + ssize_t actual_height = height > 0 ? height : (ssize_t)(-height); + + // Depending on the sign of height we generate the image top-down or bottom up + // As we iterate from the top always, if it is negative we would have to start at the end of the imaginary part and go in the oposite direction when generating + ssize_t imag_direction = height < 0 ? -1 : 1; + float c_start_real = creal(start); + float c_start_imag = height > 0 ? cimag(start) : cimag(start) + ((actual_height-1) * res);; + + + float vertical_step = imag_direction * res; + size_t position = 0; + + + float c_imag = c_start_imag; + // Simple iteration over every pixel in the picture + for(ssize_t y = 0; y < actual_height; y++){ + // Calculate imaginary part on each row as it stays the same + float c_real = c_start_real; + for(size_t x = 0; x < width; x++){ + unsigned value = converge(c_real,c_imag,n); + + // Pixel value position in the output array + + if(!color){ // Grayscale colors as specified by the assignment r = g = b + img[position] = value; + } + else{ // Temporary colors with random multipliers + img[position] = ((value*3) % 255); + img[position + 1] = ((value*2) % 255) ; + img[position + 2] = ((value*5) % 255); + } + + position += bytesPerColor; + c_real += res; + + } + c_imag += vertical_step; + } +} + +// Iterative application of the equation given by the assignment +static inline unsigned converge(float c_real, float c_imag, unsigned n) { + float real = 0.0f; + float imag = 0.0f; + + for (unsigned i = 0; i < n; i++) { + float real2 = real * real; + float imag2 = imag * imag; + + if (real2 + imag2 > 4.0f) { + return 255 - 4 * (i % 64); + } + + imag = -2.0f * real * imag + c_imag; + real = real2 - imag2 + c_real; + } + return 0; +} \ No newline at end of file diff --git a/Implementierung/tricorn_V1.h b/Implementierung/tricorn_V1.h index 403eb0a..3ddd155 100644 --- a/Implementierung/tricorn_V1.h +++ b/Implementierung/tricorn_V1.h @@ -1,5 +1,5 @@ -#include -#include -#include -#include -void tricorn_V1(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img); +#include +#include +#include +#include +void tricorn_V1(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img); \ No newline at end of file diff --git a/Implementierung/tricorn_V2.c b/Implementierung/tricorn_V2.c index 9e9a947..f6c5f8e 100644 --- a/Implementierung/tricorn_V2.c +++ b/Implementierung/tricorn_V2.c @@ -1,10 +1,11 @@ #include "tricorn_V2.h" +#include -static unsigned converge(float c_real, float c_imag, unsigned n); +static unsigned converge(float c_rea, float c_imag, unsigned n); /** - * @brief Generates a Tricorn fractal image using an optimized scalar implementation. + * @brief Generates a Tricorn fractal image using highly optimized SSE/SIMD instructions, however the algoriithm is not fully optimized. * @param start The starting complex coordinate (bottom-left or top-left depending on height sign). * @param width The width of the target image in pixels. * @param height The height of the target image. A negative height reverses the Y-axis calculation. @@ -17,48 +18,98 @@ void tricorn_V2(float complex start, size_t width, ssize_t height, float res, un int bytesPerColor = color ? 3 : 1; // Absolute height for iteration purposes - ssize_t actual_height = height > 0 ? height : (ssize_t)(-height); + ssize_t actual_height = height < 0 ? (ssize_t)(-height) : height; + size_t simd_width = (width / 4) * 4; // Depending on the sign of height we generate the image top-down or bottom up - // As we iterate from the top always, if it is negative we would have to start at the end of the imaginary part and go in the oposite direction when generating + // As we iterate the image buffer from the top, if height is negative we would have to start at the end of the imaginary part and go in the oposite direction when generating ssize_t imag_direction = height < 0 ? -1 : 1; float c_start_real = creal(start); - float c_start_imag = height > 0 ? cimag(start) : cimag(start) + ((actual_height-1) * res);; + float c_start_imag = height > 0 ? cimag(start) : cimag(start) + (actual_height - 1) * res; + __m128 four = _mm_set1_ps(4.0f); + __m128 minus_two = _mm_set1_ps(-2.0f); - float vertical_step = imag_direction * res; - size_t position = 0; + for(ssize_t y = 0; y < actual_height;y++) { + float c_imag = c_start_imag + (y * imag_direction) * res; + __m128 c_imag_vector = _mm_set1_ps(c_imag); + size_t x = 0; + 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); - float c_imag = c_start_imag; - // Simple iteration over every pixel in the picture - for(ssize_t y = 0; y < actual_height; y++){ - // Calculate imaginary part on each row as it stays the same - float c_real = c_start_real; - for(size_t x = 0; x < width; x++){ - unsigned value = converge(c_real,c_imag,n); + __m128 real = _mm_setzero_ps(); + __m128 imag = _mm_setzero_ps(); + __m128i iterations = _mm_setzero_si128(); + __m128i active_mask = _mm_set1_epi32(-1); - // Pixel value position in the output array + for(unsigned i = 0; i < n;i++) { + __m128 real2 = _mm_mul_ps(real,real); + __m128 imag2 = _mm_mul_ps(imag,imag); - if(!color){ // Grayscale colors as specified by the assignment r = g = b - img[position] = value; + __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); + + 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); } - - position += bytesPerColor; - c_real += res; - } - c_imag += vertical_step; + } } -// Iterative application of the equation given by the assignment -static inline unsigned converge(float c_real, float c_imag, unsigned n) { +static unsigned converge(float c_real, float c_imag, unsigned n) { float real = 0.0f; float imag = 0.0f; @@ -73,5 +124,5 @@ static inline unsigned converge(float c_real, float c_imag, unsigned n) { imag = -2.0f * real * imag + c_imag; real = real2 - imag2 + c_real; } - return 0; + return 0; } \ No newline at end of file diff --git a/Implementierung/tricorn_V2.h b/Implementierung/tricorn_V2.h index b9871e9..265b7fe 100644 --- a/Implementierung/tricorn_V2.h +++ b/Implementierung/tricorn_V2.h @@ -1,5 +1,6 @@ -#include -#include -#include -#include -void tricorn_V2(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img); \ No newline at end of file +#include +#include +#include +#include +#include +void tricorn_V2(float complex start, size_t width, ssize_t height, float res, unsigned n, bool color, unsigned char* img); \ No newline at end of file diff --git a/Implementierung/tricorn_V3.c b/Implementierung/tricorn_V3.c index 96974f4..e6d0489 100644 --- a/Implementierung/tricorn_V3.c +++ b/Implementierung/tricorn_V3.c @@ -1,11 +1,29 @@ #include "tricorn_V3.h" -#include -static unsigned converge(float c_rea, float c_imag, unsigned n); +static void* compute(void * args); +static unsigned converge(float c_real, float c_imag, unsigned n); +static size_t get_thread_count(); +// Argument structs passed to the threads for the caluclations +typedef struct{ + unsigned n; + _Bool color; + _Bool flipped; + float res; + size_t width; + unsigned char* img; + +} const_args; +typedef struct { + float start_r; + float start_i; + size_t start_row; + size_t stop_row; + const_args* consts; +} compute_args; /** - * @brief Generates a Tricorn fractal image using highly optimized SSE/SIMD instructions, however the algoriithm is not fully optimized. + * @brief Generates a Tricorn fractal image using an optimized SIMD + Threading implementation. * @param start The starting complex coordinate (bottom-left or top-left depending on height sign). * @param width The width of the target image in pixels. * @param height The height of the target image. A negative height reverses the Y-axis calculation. @@ -15,36 +33,88 @@ static unsigned converge(float c_rea, float c_imag, unsigned n); * @param img Pointer to the pre-allocated memory buffer where the bitmap data will be stored. */ void tricorn_V3(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img){ - int bytesPerColor = color ? 3 : 1; - + + const ssize_t min_rows_per_thread = 10; + const _Bool flipped = height < 0; + const size_t abs_height = flipped ? -height : height; + + + // Calculating the threadcount so that it is limited to a reasonable distribution where thread starting doesnt outway calculation cost + size_t thread_count = get_thread_count(); + if(abs_height >= min_rows_per_thread) + while((abs_height/thread_count) < min_rows_per_thread) thread_count--; + else thread_count = 1; + + + + const size_t y_step = (abs_height+thread_count-1)/thread_count; + const float start_r = crealf(start); + const float directional_res = (flipped ? -1.0f: 1.0f)*res; + const float start_i = height > 0 ? cimag(start) : cimag(start) + ((abs_height - 1) * res); + + const_args* consts = malloc(sizeof(*consts)); + consts->width = width; + consts->flipped = flipped; + consts->color = color; + consts->res = res; + consts->img = img; + consts->n = n; + + pthread_t threads[thread_count]; + for(size_t i = 0; i < thread_count; i++){ + compute_args* args = malloc(sizeof(*args)); + args->start_r =start_r; + args->start_i =start_i+i*directional_res*y_step; + args->start_row = i*y_step; + args->stop_row = abs_height < (args->start_row+y_step) ? abs_height: (args->start_row+y_step); + args->consts = consts; + if(pthread_create(&threads[i],NULL,compute,args)){ + compute(args); + free(args); + args = NULL; + } + } + for(size_t i = 0; i < thread_count; i++){ + pthread_join(threads[i],NULL); + } + + free(consts); + consts = NULL; + return; +} + +static void* compute(void * vargs){ + + compute_args *args = vargs; + const_args *c = args->consts; + vargs = NULL; + + + const int bytes_per_color = c->color ? 3 : 1; // Absolute height for iteration purposes - ssize_t actual_height = height < 0 ? (ssize_t)(-height) : height; - size_t simd_width = (width / 4) * 4; - - // Depending on the sign of height we generate the image top-down or bottom up - // As we iterate the image buffer from the top, if height is negative we would have to start at the end of the imaginary part and go in the oposite direction when generating - ssize_t imag_direction = height < 0 ? -1 : 1; - float c_start_real = creal(start); - float c_start_imag = height > 0 ? cimag(start) : cimag(start) + (actual_height - 1) * res; + const size_t simd_width = (c->width / 4) * 4; + float c_start_real = args->start_r; __m128 four = _mm_set1_ps(4.0f); __m128 minus_two = _mm_set1_ps(-2.0f); - for(ssize_t y = 0; y < actual_height;y++) { - float c_imag = c_start_imag + (y * imag_direction) * res; - __m128 c_imag_vector = _mm_set1_ps(c_imag); + float c_imag = args->start_i; + __m128 c_imag_vector = _mm_set1_ps(c_imag); + float four_res = 4 * c->res; + float vertical_step = (c->flipped? -1.0f: 1.0f) * c->res; + __m128 vertical_step_vector = _mm_set1_ps(vertical_step); + + for(size_t y = args->start_row; y < args->stop_row;y++) { size_t x = 0; + __m128 c_real_vector = _mm_setr_ps(c_start_real,c_start_real + 1.0f*c->res,c_start_real + 2.0f*c->res, c_start_real + 3.0f*c->res); 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); - __m128 real = _mm_setzero_ps(); __m128 imag = _mm_setzero_ps(); __m128i iterations = _mm_setzero_si128(); __m128i active_mask = _mm_set1_epi32(-1); - for(unsigned i = 0; i < n;i++) { + for(unsigned i = 0; i < c->n;i++) { __m128 real2 = _mm_mul_ps(real,real); __m128 imag2 = _mm_mul_ps(imag,imag); @@ -68,7 +138,7 @@ void tricorn_V3(float complex start, size_t width, ssize_t height, float res, un __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 max_iter = _mm_set1_epi32(c->n); __m128i is_max = _mm_cmpeq_epi32(iterations, max_iter); values = _mm_andnot_si128(is_max, values); @@ -77,38 +147,44 @@ void tricorn_V3(float complex start, size_t width, ssize_t height, float res, un for(unsigned i = 0 ; i < 4;i++) { unsigned int value = value_array[i]; - size_t position = (y*width + (x+i)) * bytesPerColor; + size_t position = (y*c->width + (x+i)) * bytes_per_color; - if(!color) { - img[position] = value; + if(!c->color) { + c->img[position] = value; } else { - img[position] = ((value*3) % 255); - img[position + 1] = ((value*2) % 255); - img[position + 2] = ((value*5) % 255); + c->img[position] = ((value*3) % 255); + c->img[position + 1] = ((value*2) % 255); + c->img[position + 2] = ((value*5) % 255); } } + c_real_vector = _mm_add_ps(c_real_vector,_mm_set1_ps(four_res)); } - for(;x < width; x++) { - float c_real = c_start_real + (x*res); - unsigned int value = converge(c_real,c_imag,n); + for(;x < c->width; x++) { + float c_real = c_start_real + (x*c->res); + unsigned int value = converge(c_real,c_imag,c->n); - size_t position = (y*width + x) * bytesPerColor; + size_t position = (y*c->width + x) * bytes_per_color; - if(!color) { - img[position] = value; + if(!c->color) { + c->img[position] = value; } else { - img[position] = ((value*3) % 255); - img[position + 1] = ((value*2) % 255); - img[position + 2] = ((value*5) % 255); + c->img[position] = ((value*3) % 255); + c->img[position + 1] = ((value*2) % 255); + c->img[position + 2] = ((value*5) % 255); } } - + c_imag += vertical_step; + c_imag_vector = _mm_add_ps(c_imag_vector,vertical_step_vector); } + free(args); + args = NULL; + return NULL; } +// Iterative application of the equation given by the assignment static unsigned converge(float c_real, float c_imag, unsigned n) { float real = 0.0f; float imag = 0.0f; @@ -125,4 +201,13 @@ static unsigned converge(float c_real, float c_imag, unsigned n) { real = real2 - imag2 + c_real; } return 0; +} +static inline size_t get_thread_count(){ + #ifdef WIN32 + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; + #else + return sysconf(_SC_NPROCESSORS_ONLN); + #endif } \ No newline at end of file diff --git a/Implementierung/tricorn_V3.h b/Implementierung/tricorn_V3.h index e18ec24..6385b12 100644 --- a/Implementierung/tricorn_V3.h +++ b/Implementierung/tricorn_V3.h @@ -1,6 +1,13 @@ -#include -#include -#include -#include -#include -void tricorn_V3(float complex start, size_t width, ssize_t height, float res, unsigned n, bool color, unsigned char* img); \ No newline at end of file +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include + +#endif +void tricorn_V3(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img); \ No newline at end of file diff --git a/Implementierung/tricorn_V4.c b/Implementierung/tricorn_V4.c deleted file mode 100644 index ebe38a0..0000000 --- a/Implementierung/tricorn_V4.c +++ /dev/null @@ -1,132 +0,0 @@ -#include "tricorn_V4.h" - -static unsigned converge(float c_real, float c_imag, unsigned n); - - -/** - * @brief Generates a Tricorn fractal image using an optimized SIMD algorithm - * @param start The starting complex coordinate (bottom-left or top-left depending on height sign). - * @param width The width of the target image in pixels. - * @param height The height of the target image. A negative height reverses the Y-axis calculation. - * @param res The step size (resolution) per pixel in the complex plane. - * @param n The maximum number of mathematical iterations per pixel. - * @param color If true, generates a 24-bit RGB image. If false, writes a grayscale image. - * @param img Pointer to the pre-allocated memory buffer where the bitmap data will be stored. - */ -void tricorn_V4(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 - ssize_t actual_height = height < 0 ? (ssize_t)(-height) : height; - size_t simd_width = (width / 4) * 4; - - // Depending on the sign of height we generate the image top-down or bottom up - // As we iterate the image buffer from the top, if height is negative we would have to start at the end of the imaginary part and go in the oposite direction when generating - ssize_t imag_direction = height < 0 ? -1 : 1; - float c_start_real = creal(start); - float c_start_imag = height > 0 ? cimag(start) : cimag(start) + (actual_height - 1) * res; - - __m128 four = _mm_set1_ps(4.0f); - __m128 minus_two = _mm_set1_ps(-2.0f); - - float c_imag = c_start_imag; - __m128 c_imag_vector = _mm_set1_ps(c_imag); - - float four_res = 4 * res; - - float vertical_step = imag_direction * res; - __m128 vertical_step_vector = _mm_set1_ps(vertical_step); - - for(ssize_t y = 0; y < actual_height;y++) { - size_t x = 0; - __m128 c_real_vector = _mm_setr_ps(c_start_real,c_start_real + 1.0f*res,c_start_real + 2.0f*res, c_start_real + 3.0f*res); - for(; x < simd_width;x += 4) { //SIMD Loop - __m128 real = _mm_setzero_ps(); - __m128 imag = _mm_setzero_ps(); - __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_cmple_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); - } - - - __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); - } - } - c_real_vector = _mm_add_ps(c_real_vector,_mm_set1_ps(four_res)); - } - - 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 + 2] = ((value*5) % 255); - } - } - c_imag += vertical_step; - c_imag_vector = _mm_add_ps(c_imag_vector,vertical_step_vector); - } -} - -static unsigned converge(float c_real, float c_imag, unsigned n) { - float real = 0.0f; - float imag = 0.0f; - - for (unsigned i = 0; i < n; i++) { - float real2 = real * real; - float imag2 = imag * imag; - - if (real2 + imag2 > 4.0f) { - return 255 - 4 * (i % 64); - } - - imag = -2.0f * real * imag + c_imag; - real = real2 - imag2 + c_real; - } - return 0; -} \ No newline at end of file diff --git a/Implementierung/tricorn_V4.h b/Implementierung/tricorn_V4.h deleted file mode 100644 index 2967a44..0000000 --- a/Implementierung/tricorn_V4.h +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -#include -#include -#include -#include -void tricorn_V4(float complex start, size_t width, ssize_t height, float res, unsigned n, bool color, unsigned char* img); \ No newline at end of file diff --git a/Implementierung/tricorn_V5.h b/Implementierung/tricorn_V5.h deleted file mode 100644 index bafd13c..0000000 --- a/Implementierung/tricorn_V5.h +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include -#include -#include -#include -#ifdef WIN32 -#include -#else -#include - -#endif -void tricorn_V5(float complex start, size_t width, ssize_t height, float res, unsigned n, _Bool color, unsigned char* img); \ No newline at end of file