1
0

add math_func_checker.c

This commit is contained in:
Rokas Puzonas 2025-01-16 22:30:35 +02:00
parent 46578d7ce9
commit 873e8c4cb7
6 changed files with 197 additions and 368 deletions

View File

@ -6,24 +6,25 @@ guess_cpu_speed: build/guess_cpu_speed
gen_data: build/gen_data
main: build/main
build/gen_data: src/gen_data.c
build:
mkdir -p build
build/gen_data: src/gen_data.c build
gcc -o build/gen_data src/gen_data.c $(CFLAGS)
build/main: src/main.c src/json_parser.c src/rprof.h
mkdir -p build
build/math_func_checker: src/math_func_checker.c build
gcc -o build/math_func_checker src/math_func_checker.c $(CFLAGS)
build/main: src/main.c src/json_parser.c src/rprof.h build
gcc -o build/main src/main.c $(CFLAGS)
build/test: src/tests.c src/repetition_tester.c
mkdir -p build
build/test: src/tests.c src/repetition_tester.c build
gcc -o build/test src/tests.c $(CFLAGS)
build/guess_cpu_speed: src/guess_cpu_speed.c src/timer.c
mkdir -p build
build/guess_cpu_speed: src/guess_cpu_speed.c build
gcc -o build/guess_cpu_speed src/guess_cpu_speed.c $(CFLAGS)
build/page_fault_prober: src/page_fault_prober.c
mkdir -p build
build/page_fault_prober: src/page_fault_prober.c build
gcc -o build/page_fault_prober src/page_fault_prober.c $(CFLAGS)
clean:

View File

@ -1,14 +1,64 @@
#include <float.h>
#include <math.h>
#include <sys/param.h>
typedef double f64;
#define EARTH_RADIUS 6372.8
struct counters {
f64 min_input;
f64 max_input;
f64 min_output;
f64 max_output;
};
#define EMPTY_COUNTERS (struct counters){ .min_input = DBL_MAX, .max_input = -DBL_MAX, .min_output = DBL_MAX, .max_output = -DBL_MAX }
struct counters cos_counters = EMPTY_COUNTERS;
struct counters sin_counters = EMPTY_COUNTERS;
struct counters asin_counters = EMPTY_COUNTERS;
struct counters sqrt_counters = EMPTY_COUNTERS;
#define LOG_COUNTERS(counters, func, a) do { \
countres.min_input = MIN(countres.min_input, a) \
} while (0)
static f64 log_counters(struct counters *counters, f64 (*calc)(f64), f64 input)
{
counters->min_input = MIN(counters->min_input, input);
counters->max_input = MAX(counters->max_input, input);
f64 output = calc(input);
counters->min_output = MIN(counters->min_output, output);
counters->max_output = MAX(counters->max_output, output);
return output;
}
static f64 sqaure_f64(f64 num)
{
return num*num;
}
static f64 my_sin(f64 num)
{
return log_counters(&sin_counters, sin, num);
}
static f64 my_cos(f64 num)
{
return log_counters(&cos_counters, cos, num);
}
static f64 my_sqrt(f64 num)
{
return log_counters(&sqrt_counters, sqrt, num);
}
static f64 my_asin(f64 num)
{
return log_counters(&asin_counters, asin, num);
}
static f64 radians_to_degrees(f64 degrees)
{
return 0.01745329251994329577 * degrees;
@ -26,8 +76,8 @@ static f64 get_harvensine_distance(f64 X0, f64 Y0, f64 X1, f64 Y1, f64 earth_rad
lat1 = radians_to_degrees(lat1);
lat2 = radians_to_degrees(lat2);
f64 a = sqaure_f64(sin(dLat/2.0)) + cos(lat1)*cos(lat2)*sqaure_f64(sin(dLon/2));
f64 c = 2.0*asin(sqrt(a));
f64 a = sqaure_f64(my_sin(dLat/2.0)) + my_cos(lat1)*my_cos(lat2)*sqaure_f64(my_sin(dLon/2));
f64 c = 2.0*my_asin(my_sqrt(a));
return earth_radius * c;
}

View File

@ -5,7 +5,6 @@
#include <time.h>
#include <math.h>
#include "harvensine_compute.h"
#include "json_parser.c"
#include "harvensine_formula.c"
@ -207,5 +206,23 @@ int main(int argc, char **argv)
rprof_output(rprof_cmp_by_exclusive_duration);
printf("\n------- Counters ------\n");
printf("Sin:\n");
printf("Input: %f - %f\n", sin_counters.min_input, sin_counters.max_input);
printf("Output: %f - %f\n\n", sin_counters.min_output, sin_counters.max_output);
printf("Cos:\n");
printf("Input: %f - %f\n", cos_counters.min_input, cos_counters.max_input);
printf("Output: %f - %f\n\n", cos_counters.min_output, cos_counters.max_output);
printf("Sqrt:\n");
printf("Input: %f - %f\n", sqrt_counters.min_input, sqrt_counters.max_input);
printf("Output: %f - %f\n\n", sqrt_counters.min_output, sqrt_counters.max_output);
printf("Asin:\n");
printf("Input: %f - %f\n", asin_counters.min_input, asin_counters.max_input);
printf("Output: %f - %f\n", asin_counters.min_output, asin_counters.max_output);
return 0;
}

117
src/math_func_checker.c Normal file
View File

@ -0,0 +1,117 @@
#include <math.h>
#include <inttypes.h>
#include <stdio.h>
#include <immintrin.h>
typedef double f64;
typedef float f32;
typedef f64 (*math_func)(f64);
#define ARRAY_LEN(x) (sizeof(x)/sizeof(x[0]))
struct math_spec {
const char *name;
math_func mine;
math_func reference;
f64 from;
f64 to;
};
static f64 my_sin(f64 num)
{
return num;
}
static f64 my_cos(f64 num)
{
return num;
}
static f64 my_sqrt1(f64 num)
{
__m128d zero = _mm_set_sd(0);
__m128d mm_num = _mm_set_sd(num);
__m128d mm_result = _mm_sqrt_sd(zero, mm_num);
f64 result = _mm_cvtsd_f64(mm_result);
return result;
}
static f64 my_sqrt2(f64 num)
{
__m128 mm_num = _mm_set_ss(num);
__m128 mm_inv_result = _mm_rsqrt_ss(mm_num);
__m128 mm_result = _mm_div_ss(_mm_set_ss(1), mm_inv_result);
f32 result = _mm_cvtss_f32(mm_result);
return result;
}
static f64 my_asin(f64 num)
{
return num;
}
int main()
{
uint64_t samples = 10000;
struct math_spec specs[] = {
{
.name = "sin",
.mine = my_sin,
.reference = sin,
.from = -M_PI,
.to = M_PI
},
{
.name = "cos",
.mine = my_cos,
.reference = cos,
.from = -M_PI/2,
.to = M_PI/2
},
{
.name = "sqrt",
.mine = my_sqrt1,
.reference = sqrt,
.from = 0,
.to = 1,
},
{
.name = "sqrt (using rsqrt)",
.mine = my_sqrt2,
.reference = sqrt,
.from = 0,
.to = 1,
},
{
.name = "asin",
.mine = my_asin,
.reference = asin,
.from = 0,
.to = 1,
}
};
for (int i = 0; i < ARRAY_LEN(specs); i++) {
struct math_spec spec = specs[i];
f64 max_error = 0;
for (int j = 0; j < samples; j++) {
f64 input = ((f64)j / samples) * (spec.to - spec.from) + spec.from;
f64 reference_output = spec.reference(input);
f64 my_output = spec.mine(input);
f64 error = reference_output - my_output;
if (error < 0) {
error = -error;
}
if (error > max_error) {
max_error = error;
}
}
printf("%s max error %f\n", spec.name, max_error);
}
return 0;
}

View File

@ -1,186 +0,0 @@
#include <inttypes.h>
#include <string.h>
#include <sys/resource.h>
#include "rprof.h"
enum tester_state {
STATE_UNKNOWN,
STATE_RUNNING,
STATE_FINISHED,
STATE_ERROR
};
struct repetition_tester {
enum tester_state state;
uint64_t cpu_freq;
uint32_t testing_timeout;
uint64_t expected_byte_count;
uint64_t last_found_min_time;
bool show_current_min;
uint32_t closed_blocks;
uint32_t opened_blocks;
uint64_t test_running_time;
uint64_t test_running_bytes;
uint64_t test_running_page_faults;
};
struct repetition_results {
uint64_t test_count;
uint64_t total_time;
uint64_t min_time;
uint64_t max_time;
uint64_t total_page_faults;
uint64_t min_page_faults;
uint64_t max_page_faults;
};
typedef void (*test_cb)(struct repetition_tester*, void *user);
void repetition_tester_init(struct repetition_tester *tester) {
tester->cpu_freq = rprof_get_cpu_timer_hz(100);
tester->testing_timeout = 10;
}
void repetition_tester_error(struct repetition_tester *tester, char *message) {
tester->state = STATE_ERROR;
printf("Error during repetition: %s\n", message);
}
void print_repetition_time(char *label, float cpu_time, uint64_t bytes, uint64_t page_faults, uint64_t cpu_freq) {
printf("%s: %.0f", label, cpu_time);
if (cpu_freq) {
float seconds_time = (float)cpu_time/(float)cpu_freq;
printf(" (%fms)", seconds_time*1000.0);
if (bytes) {
float gigabyte = 1024 * 1024 * 1024;
float bandwidth = bytes / (gigabyte * seconds_time);
printf(" at %fgb/s", bandwidth);
}
}
if (page_faults) {
printf(" PF: %lu", page_faults);
if (bytes) {
printf(" (%.3fkb/fault)", ((float)bytes)/(page_faults * 1024));
}
}
}
bool repetition_tester_continue(struct repetition_tester *tester, struct repetition_results *results) {
if (tester->state != STATE_RUNNING) {
return false;
}
if (tester->opened_blocks == 0) {
return true;
}
if (tester->opened_blocks != tester->closed_blocks) {
repetition_tester_error(tester, "Closed blocks doesn't match opened blocks");
return false;
}
if (tester->expected_byte_count != tester->test_running_bytes) {
repetition_tester_error(tester, "Processed byte coutn doesn't match");
return false;
}
uint64_t now = rprof_read_cpu_timer();
results->test_count += 1;
results->total_time += tester->test_running_time;
results->max_time = MAX(results->max_time, tester->test_running_time);
if (tester->test_running_time < results->min_time) {
results->min_time = tester->test_running_time;
tester->last_found_min_time = now;
if (tester->show_current_min) {
printf("\r");
print_repetition_time("Min", tester->test_running_time, tester->test_running_bytes, 0, tester->cpu_freq);
fflush(stdout);
}
}
results->total_page_faults += tester->test_running_page_faults;
results->min_page_faults = MIN(results->min_page_faults, tester->test_running_page_faults);
results->max_page_faults = MAX(results->max_page_faults, tester->test_running_page_faults);
tester->opened_blocks = 0;
tester->closed_blocks = 0;
tester->test_running_bytes = 0;
tester->test_running_time = 0;
tester->test_running_page_faults = 0;
uint64_t time_since_last_find = (now - tester->last_found_min_time);
if ((time_since_last_find / tester->cpu_freq) >= tester->testing_timeout) {
tester->state = STATE_FINISHED;
return false;
}
return true;
}
int repetition_test_run(struct repetition_tester *tester, uint64_t expected_byte_count, struct repetition_results *results, test_cb cb, void *user) {
memset(results, 0, sizeof(struct repetition_results));
tester->state = STATE_RUNNING;
tester->expected_byte_count = expected_byte_count;
tester->last_found_min_time = rprof_read_cpu_timer();
results->min_time = UINT64_MAX;
results->max_time = 0;
results->test_count = 0;
results->total_time = 0;
results->max_page_faults = 0;
results->min_page_faults = UINT64_MAX;
results->total_page_faults = 0;
while (repetition_tester_continue(tester, results)) {
cb(tester, user);
}
if (tester->show_current_min) {
printf("\n");
}
if (tester->state == STATE_ERROR) {
return -1;
}
return 0;
}
void print_repetition_results(struct repetition_tester *tester, struct repetition_results *results) {
print_repetition_time("Min", results->min_time, tester->expected_byte_count, results->min_page_faults, tester->cpu_freq);
printf("\n");
print_repetition_time("Max", results->max_time, tester->expected_byte_count, results->max_page_faults, tester->cpu_freq);
printf("\n");
if (results->test_count) {
uint64_t avg_time = results->total_time/results->test_count;
uint64_t avg_page_faults = results->total_page_faults/results->test_count;
print_repetition_time("Avg", avg_time, tester->expected_byte_count, avg_page_faults, tester->cpu_freq);
printf("\n");
}
}
void repetition_time_start(struct repetition_tester *tester) {
tester->opened_blocks++;
tester->test_running_time -= rprof_read_cpu_timer();
tester->test_running_page_faults -= read_page_faults();
}
void repetition_time_end(struct repetition_tester *tester) {
tester->closed_blocks++;
tester->test_running_time += rprof_read_cpu_timer();
tester->test_running_page_faults += read_page_faults();
}
void repetition_count_bytes(struct repetition_tester *tester, int bytes) {
tester->test_running_bytes += bytes;
}

View File

@ -1,170 +0,0 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "repetition_tester.c"
#define RPROF_IMPLEMENTATION
#include "rprof.h"
#define ARRAY_LEN(x) (sizeof(x)/sizeof(x[0]))
size_t get_file_size(char *filename)
{
FILE *f = fopen(filename, "r");
if (f == NULL) { return 0; }
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
fclose(f);
return size;
}
struct test_params {
char *filename;
uint8_t *buffer;
uint64_t byte_count;
bool inner_malloc;
};
struct testcase {
char *name;
test_cb cb;
bool inner_malloc;
};
void handle_buffer_malloc(struct test_params *params) {
if (params->inner_malloc) {
params->buffer = malloc(sizeof(uint8_t) * params->byte_count);
}
}
void handle_buffer_free(struct test_params *params) {
if (params->inner_malloc) {
free(params->buffer);
}
}
void test_write_bytes(struct repetition_tester *tester, void *user)
{
struct test_params *params = user;
handle_buffer_malloc(params);
repetition_time_start(tester);
for (uint64_t i = 0; i < params->byte_count; ++i) {
params->buffer[i] = (uint8_t)i;
}
repetition_time_end(tester);
repetition_count_bytes(tester, params->byte_count);
handle_buffer_free(params);
}
void test_fread(struct repetition_tester *tester, void *user)
{
struct test_params *params = user;
FILE *f = fopen(params->filename, "r");
if (f == NULL) {
repetition_tester_error(tester, "Failed to open file");
return;
}
handle_buffer_malloc(params);
repetition_time_start(tester);
int result = fread(params->buffer, params->byte_count, 1, f);
repetition_time_end(tester);
if (result <= 0) {
repetition_tester_error(tester, "Failed to read file");
goto err;
}
repetition_count_bytes(tester, params->byte_count);
err:
handle_buffer_free(params);
fclose(f);
}
void test_read(struct repetition_tester *tester, void *user)
{
struct test_params *params = user;
int fd = open(params->filename, O_RDONLY);
if (fd <= 0) {
repetition_tester_error(tester, "Failed to open file");
return;
}
handle_buffer_malloc(params);
repetition_time_start(tester);
int result = read(fd, params->buffer, params->byte_count);
repetition_time_end(tester);
if (result <= 0) {
repetition_tester_error(tester, "Failed to read file");
goto err;
}
repetition_count_bytes(tester, result);
err:
handle_buffer_free(params);
close(fd);
}
int main()
{
struct repetition_tester tester = { 0 };
repetition_tester_init(&tester);
tester.show_current_min = true;
char *filename = "data_10000000.json";
uint64_t file_size = get_file_size(filename);
printf("File size: %lu\n", file_size);
struct testcase cases[] = {
{ .name = "write bytes", .cb = test_write_bytes, .inner_malloc = false },
{ .name = "write bytes + malloc", .cb = test_write_bytes, .inner_malloc = true },
{ .name = "read", .cb = test_read, .inner_malloc = false },
{ .name = "read + malloc", .cb = test_read, .inner_malloc = true },
{ .name = "fread", .cb = test_fread, .inner_malloc = false },
{ .name = "fread + malloc", .cb = test_fread, .inner_malloc = true },
};
struct repetition_results results = { 0 };
while (true) {
for (int i = 0; i < ARRAY_LEN(cases); i++) {
struct testcase *testcase = &cases[i];
uint8_t *buffer = NULL;
if (!testcase->inner_malloc) {
buffer = malloc(sizeof(uint8_t) * file_size);
}
struct test_params params = {
.filename = filename,
.buffer = buffer,
.byte_count = file_size,
.inner_malloc = testcase->inner_malloc
};
if (repetition_test_run(&tester, file_size, &results, testcase->cb, &params)) {
return -1;
}
printf("----- %s -----\n", testcase->name);
print_repetition_results(&tester, &results);
printf("\n");
if (!testcase->inner_malloc) {
free(buffer);
}
}
}
return 0;
}