Refactored file and fuction names
Now: tricorn - Optimized SIMD main implementation tricorn_V1 - Optimized SISD comparison implementation tricorn_V0 - Unoptimized SIMD implementation tricorn_V5 - highly optimized SIMD + threading implementation
This commit is contained in:
+35
-115
@@ -1,29 +1,11 @@
|
||||
#include "tricorn.h"
|
||||
#include <immintrin.h>
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user