WIP, but almost done

This commit is contained in:
2025-11-19 15:56:00 +02:00
parent 2d3ebea187
commit 0e84d04e1e
6 changed files with 514 additions and 129 deletions
+165 -129
View File
@@ -1,8 +1,6 @@
#include <errno.h>
#include <math.h>
#include <png.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -13,7 +11,17 @@ float noise(float x, float y);
float linear_interpolation(float a, float b, float t);
float quintic_curve(float t);
float dot_product(float a[], float b[]);
float *pseudo_random_gradient_vector_get(float x, float y);
void pseudo_random_gradient_vector_get(float vec[2], float x, float y);
void *calculate(void *args);
void single_threaded(unsigned char *buffer, int height, int width);
void multi_threaded(unsigned char *buffer, int height, int width,
int thread_no);
void panic(const char *message);
void benchmark_calculations(unsigned char *buffer, int height, int width,
int thread_no);
void write_image_with_timing(png_image *img, const unsigned char *buffer);
void print_timediff(const char *message, struct timespec start,
struct timespec end);
static unsigned char random_bytes[1024];
@@ -34,7 +42,117 @@ struct multi_threaded_arg {
int width;
};
void *calculate(void *args) { // TODO: reorder functions by call order
int main(int argc, char *argv[]) {
int n = getrandom(random_bytes, 1024, 0);
if (n != 1024) {
fprintf(stderr, "err: getrandom didn't work out as planned\n");
return 1;
}
if (argc < 3) {
fprintf(stderr, "usage: width height [threads] \n");
return 1;
}
int width = atoi(argv[1]), height = atoi(argv[2]);
int threads = 1;
if (argc == 4) {
threads = atoi(argv[3]);
if (height < threads) {
fprintf(stderr,
"err: height < threads. expected: height >= threads. aborting\n");
return 1;
}
printf("rows per thread: %u\n", height / threads);
}
png_image img;
memset(&img, 0, sizeof(img));
img.format = PNG_FORMAT_GRAY;
img.width = width;
img.height = height;
img.version = PNG_IMAGE_VERSION;
unsigned char *buffer;
buffer = malloc(PNG_IMAGE_SIZE(img));
benchmark_calculations(buffer, height, width, threads);
write_image_with_timing(&img, buffer);
free(buffer);
}
void benchmark_calculations(unsigned char *buffer, int height, int width,
int threads) {
struct timespec start, end;
for (int i = 0; i < 3; i++) {
printf("performing warmup run #%u, stay tuned!\n", i + 1);
clock_gettime(CLOCK_MONOTONIC, &start);
if (threads < 2) {
single_threaded(buffer, height, width);
} else {
multi_threaded(buffer, height, width, threads);
}
clock_gettime(CLOCK_MONOTONIC, &end);
print_timediff("warmup calculation time: ", start, end);
}
puts("performing 10 runs!");
for (int i = 0; i < 10; i++) {
clock_gettime(CLOCK_MONOTONIC, &start);
if (threads < 2) {
single_threaded(buffer, height, width);
} else {
multi_threaded(buffer, height, width, threads);
}
clock_gettime(CLOCK_MONOTONIC, &end);
print_timediff("calculation time: ", start, end);
}
}
void single_threaded(unsigned char *buffer, int height, int width) {
struct single_threaded_arg arg = {
.type = THRD_SINGLE, .buffer = buffer, .height = height, .width = width};
calculate(&arg);
}
void multi_threaded(unsigned char *buffer, int height, int width,
int thread_no) {
pthread_t threads[thread_no];
const unsigned int rows_per_thread = height / thread_no;
struct multi_threaded_arg *all_args[thread_no];
for (int i = 0; i < thread_no; i++) {
const int row_start = i * rows_per_thread;
int row_end = (i + 1) * rows_per_thread;
if (i == thread_no - 1) {
row_end = height;
}
struct multi_threaded_arg *arg;
arg = malloc(sizeof *arg);
*arg = (struct multi_threaded_arg){.type = THRD_MULTI,
.buffer = buffer,
.height_start = row_start,
.height_end = row_end,
.width = width};
pthread_create(&threads[i], NULL, calculate, arg);
all_args[i] = arg;
}
for (int i = 0; i < thread_no; ++i) {
pthread_join(threads[i], NULL);
free(all_args[i]);
}
}
void *calculate(void *args) {
int x = 0;
int height = 0, width = 0;
unsigned char *buffer;
@@ -56,15 +174,17 @@ void *calculate(void *args) { // TODO: reorder functions by call order
}
}
/* minimum space needed for any variation of error msg */
char msg[31 + 13 + 11 + 1];
if (height == 0 || width == 0) {
printf("things have got seriously wrong!");
strcat(msg, "things have got seriously wrong");
if (height == 0) {
printf(" height is 0!");
strcat(msg, " height is 0!");
} else {
printf(" width is 0!");
strcat(msg, " width is 0!");
}
printf(" aborting\n");
exit(1);
strcat(msg, " aborting\n");
panic(msg);
}
for (; x < height; ++x) {
@@ -79,101 +199,9 @@ void *calculate(void *args) { // TODO: reorder functions by call order
return NULL;
}
void single_threaded(unsigned char *buffer, int height, int width) {
struct single_threaded_arg arg = {
.type = THRD_SINGLE, .buffer = buffer, .height = height, .width = width};
calculate(&arg);
}
void multi_threaded(unsigned char *buffer, int height, int width,
int thread_no) {
if (thread_no == 1) {
single_threaded(buffer, height, width);
return;
}
pthread_t threads[thread_no];
const unsigned int rows_per_thread = height / thread_no;
printf("rows per thread: %u\n", rows_per_thread);
struct multi_threaded_arg *args_arr[thread_no];
for (int i = 0; i < thread_no; i++) {
const int height_start = i * rows_per_thread;
int height_end = (i + 1) * rows_per_thread;
if (i == thread_no - 1) {
height_end = height;
}
struct multi_threaded_arg *arg;
arg = malloc(sizeof *arg);
*arg = (struct multi_threaded_arg){.type = THRD_MULTI,
.buffer = buffer,
.height_start = height_start,
.height_end = height_end,
.width = width};
pthread_create(&threads[i], NULL, calculate, arg);
args_arr[i] = arg;
}
for (int i = 0; i < thread_no; ++i) {
pthread_join(threads[i], NULL);
free(args_arr[i]);
}
}
int main(int argc, char *argv[]) {
int n = getrandom(random_bytes, 1024, 0);
if (n != 1024) {
fprintf(stderr, "err: getrandom didn't work out as planned\n");
return 1;
}
if (argc < 3) {
fprintf(stderr, "err: must supply 3 params!\n");
return 1;
}
int width = atoi(argv[1]), height = atoi(argv[2]);
int threads = 1;
if (argc == 4) {
threads = atoi(argv[3]);
if (height < threads) {
fprintf(stderr,
"err: rows < threads. expected: rows >= threads. aborting\n");
return 1;
}
}
png_image img;
memset(&img, 0, sizeof(img));
img.format = PNG_FORMAT_GRAY;
img.width = width;
img.height = height;
img.version = PNG_IMAGE_VERSION;
unsigned char *buffer;
printf("PNG IMAGE SIZE: %u bytes\n", PNG_IMAGE_SIZE(img));
buffer = malloc(PNG_IMAGE_SIZE(img));
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
if (threads < 2)
single_threaded(buffer, height, width);
else
multi_threaded(buffer, height, width, threads);
clock_gettime(CLOCK_MONOTONIC, &end);
printf("calculation time: %fs.\n", (double)(end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1e9);
clock_gettime(CLOCK_MONOTONIC, &start);
png_image_write_to_file(&img, "noise.png", 0, buffer, 0, NULL);
clock_gettime(CLOCK_MONOTONIC, &end);
printf("write to file time (single-threaded): %fs.\n",
(double)(end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1e9);
free(buffer);
void panic(const char *message) {
fputs(message, stderr);
exit(1);
}
float noise(float x, float y) {
@@ -183,12 +211,12 @@ float noise(float x, float y) {
float point_in_quad_x = x - left;
float point_in_quad_y = y - top;
float *top_left_gradient = pseudo_random_gradient_vector_get(left, top);
float *top_right_gradient = pseudo_random_gradient_vector_get(left + 1, top);
float *bottom_left_gradient =
pseudo_random_gradient_vector_get(left, top + 1);
float *bottom_right_gradient =
pseudo_random_gradient_vector_get(left + 1, top + 1);
float top_left_gradient[2], top_right_gradient[2], bottom_left_gradient[2],
bottom_right_gradient[2];
pseudo_random_gradient_vector_get(top_left_gradient, left, top);
pseudo_random_gradient_vector_get(top_right_gradient, left + 1, top);
pseudo_random_gradient_vector_get(bottom_left_gradient, left, top + 1);
pseudo_random_gradient_vector_get(bottom_right_gradient, left + 1, top + 1);
float distance_to_top_left[] = {point_in_quad_x, point_in_quad_y};
float distance_to_top_right[] = {point_in_quad_x - 1, point_in_quad_y};
@@ -207,11 +235,6 @@ float noise(float x, float y) {
float bx = linear_interpolation(bx1, bx2, point_in_quad_x);
float tb = linear_interpolation(tx, bx, point_in_quad_y);
free(top_left_gradient);
free(top_right_gradient);
free(bottom_left_gradient);
free(bottom_right_gradient);
return tb;
}
@@ -223,10 +246,7 @@ float quintic_curve(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
float dot_product(float a[2], float b[2]) { return a[0] * b[0] + a[1] * b[1]; }
float *pseudo_random_gradient_vector_get(float x, float y) {
float *a;
a = malloc(sizeof *a * 2);
void pseudo_random_gradient_vector_get(float vec[2], float x, float y) {
int xi = (int)x & 1023;
int yi = (int)y & 1023;
@@ -235,22 +255,38 @@ float *pseudo_random_gradient_vector_get(float x, float y) {
switch (v) {
case 0:
a[0] = 1;
a[1] = 0;
vec[0] = 1;
vec[1] = 0;
break;
case 1:
a[0] = -1;
a[1] = 0;
vec[0] = -1;
vec[1] = 0;
break;
case 2:
a[0] = 0;
a[1] = 1;
vec[0] = 0;
vec[1] = 1;
break;
case 3:
a[0] = 0;
a[1] = -1;
vec[0] = 0;
vec[1] = -1;
break;
}
return a;
}
void write_image_with_timing(png_image *img, const unsigned char *buffer) {
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
png_image_write_to_file(img, "noise.png", 0, buffer, 0, NULL);
clock_gettime(CLOCK_MONOTONIC, &end);
print_timediff("write to file time (single-threaded): ", start, end);
}
void print_timediff(const char *message, struct timespec start,
struct timespec end) {
double secs = end.tv_sec - start.tv_sec;
double nsecs = (end.tv_nsec - start.tv_nsec) / 1e9;
printf("%s%fs.\n", message, secs + nsecs);
}