add math_func_checker.c
This commit is contained in:
parent
46578d7ce9
commit
873e8c4cb7
19
Makefile
19
Makefile
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
19
src/main.c
19
src/main.c
@ -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
117
src/math_func_checker.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
170
src/tests.c
170
src/tests.c
@ -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, ¶ms)) {
|
||||
return -1;
|
||||
}
|
||||
printf("----- %s -----\n", testcase->name);
|
||||
print_repetition_results(&tester, &results);
|
||||
printf("\n");
|
||||
|
||||
if (!testcase->inner_malloc) {
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user