From 828de96adda598c738dfd4634611be1215320ceb Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Wed, 30 Aug 2023 00:18:00 +0300 Subject: [PATCH] add repetition tester --- Makefile | 4 + compile_flags.txt | 1 + src/main.c | 5 +- src/repetition_tester.c | 162 ++++++++++++++++++++++++++++++++++++++++ src/rprof.h | 1 + src/tests.c | 119 +++++++++++++++++++++++++++++ 6 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 compile_flags.txt create mode 100644 src/repetition_tester.c create mode 100644 src/tests.c diff --git a/Makefile b/Makefile index e115e23..dd0eae8 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,10 @@ build/main: src/main.c src/json_parser.c src/rprof.h mkdir -p build gcc -o build/main src/main.c $(CFLAGS) +build/test: src/tests.c src/repetition_tester.c + mkdir -p build + gcc -o build/test src/tests.c $(CFLAGS) + build/guess_cpu_speed: src/guess_cpu_speed.c src/timer.c mkdir -p build gcc -o build/guess_cpu_speed src/guess_cpu_speed.c $(CFLAGS) diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..909f5e9 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1 @@ +-DRPROF_IMPLEMENTATION diff --git a/src/main.c b/src/main.c index 05c817c..7c6cc67 100644 --- a/src/main.c +++ b/src/main.c @@ -5,13 +5,14 @@ #include #include -#define RPROF_IMPLEMENTATION #include "harvensine_compute.h" -#include "rprof.h" #include "json_parser.c" #include "harvensine_formula.c" +#define RPROF_IMPLEMENTATION +#include "rprof.h" + struct point_pair { f64 x0; f64 y0; diff --git a/src/repetition_tester.c b/src/repetition_tester.c new file mode 100644 index 0000000..04af939 --- /dev/null +++ b/src/repetition_tester.c @@ -0,0 +1,162 @@ +#include +#include +#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; +}; + +struct repetition_results { + uint64_t test_count; + uint64_t total_time; + uint64_t min_time; + uint64_t max_time; +}; + +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 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); + } + } +} + +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, tester->cpu_freq); + fflush(stdout); + } + } + + tester->opened_blocks = 0; + tester->closed_blocks = 0; + tester->test_running_bytes = 0; + tester->test_running_time = 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; + + 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, tester->cpu_freq); + printf("\n"); + + print_repetition_time("Max", results->max_time, tester->expected_byte_count, tester->cpu_freq); + printf("\n"); + + if (results->test_count) { + uint64_t avg_time = results->total_time/results->test_count; + print_repetition_time("Avg", avg_time, tester->expected_byte_count, tester->cpu_freq); + printf("\n"); + } +} + +void repetition_time_start(struct repetition_tester *tester) { + tester->opened_blocks++; + tester->test_running_time -= rprof_read_cpu_timer(); +} + +void repetition_time_end(struct repetition_tester *tester) { + tester->closed_blocks++; + tester->test_running_time += rprof_read_cpu_timer(); +} + +void repetition_count_bytes(struct repetition_tester *tester, int bytes) { + tester->test_running_bytes += bytes; +} diff --git a/src/rprof.h b/src/rprof.h index fd6d251..4cf72cd 100644 --- a/src/rprof.h +++ b/src/rprof.h @@ -76,6 +76,7 @@ void rprof_output(prof_sort_cmp_cb sort_cb); #define RPROF_START_BYTES(label, bytes) rprof_start(__COUNTER__, label, bytes) #define RPROF_STOP() rprof_stop() +#define RPROF_IMPLEMENTATION #ifdef RPROF_IMPLEMENTATION // ------------------------ CPU Timing ------------------------- diff --git a/src/tests.c b/src/tests.c new file mode 100644 index 0000000..b0c4ce5 --- /dev/null +++ b/src/tests.c @@ -0,0 +1,119 @@ +#include +#include +#include + +#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; +}; + +struct testcase { + char *name; + test_cb cb; +}; + +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; + } + + 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: + 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; + } + + 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: + 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); + + uint8_t *buffer = malloc(sizeof(uint8_t) * file_size); + struct test_params params = { + .filename = filename, + .buffer = buffer, + .byte_count = file_size + }; + + struct testcase cases[] = { + { .name = "fread", .cb = test_fread }, + { .name = "read", .cb = test_read }, + }; + + struct repetition_results results = { 0 }; + while (true) { + for (int i = 0; i < ARRAY_LEN(cases); i++) { + struct testcase *testcase = &cases[i]; + + 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"); + } + } + + free(buffer); + + return 0; +}