From 4b1e8720e3ec34624631406dfac8f469409fa9cb Mon Sep 17 00:00:00 2001 From: doylet Date: Sun, 3 Mar 2024 23:46:35 +1100 Subject: [PATCH] Add the repetition tester --- part3/base.h | 5 +- part3/repetition_tester.c | 298 +++++++++++++++++++++++++++++++++++++- project.rdbg | Bin 1191 -> 1201 bytes 3 files changed, 301 insertions(+), 2 deletions(-) diff --git a/part3/base.h b/part3/base.h index d4b6990..9efc8a8 100644 --- a/part3/base.h +++ b/part3/base.h @@ -21,6 +21,9 @@ typedef float f32; typedef double f64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; typedef uint64_t u64; // NOTE: Globals /////////////////////////////////////////////////////////////////////////////////// @@ -47,7 +50,7 @@ typedef struct Str8BinarySplitResult { Str8 rhs; } Str8BinarySplitResult; -#define STR8(string) (Str8){.data = (string), .size = ARRAY_UCOUNT(string) - 1 } +#define STR8(string) (Str8){.data = (char *)(string), .size = ARRAY_UCOUNT(string) - 1 } #define STR8_FMT(string) (int)((string).size), (string).data bool Str8_Equals(Str8 lhs, Str8 rhs); diff --git a/part3/repetition_tester.c b/part3/repetition_tester.c index 5241c45..cc11ca0 100644 --- a/part3/repetition_tester.c +++ b/part3/repetition_tester.c @@ -1,12 +1,308 @@ +#define _CRT_SECURE_NO_WARNINGS #include #include #include +#include +#include +#include #include "base.h" #include "listing_0074_platform_metrics.cpp" #include "base.c" -int main() +typedef enum RepTesterMode { + RepTesterMode_Nil, + RepTesterMode_Testing, + RepTesterMode_Error, + RepTesterMode_Complete, +} RepTesterMode; + +typedef struct RepTesterResults { + u64 test_count; + u64 total_time; + u64 max_time; + u64 min_time; +} RepTesterResults; + +typedef struct RepTester { + RepTesterMode mode; + u32 open_block_count; + u32 close_block_count; + u64 cpu_timer_freq; + RepTesterResults results; + + size_t total_bytes_read; + size_t desired_bytes_read; + + u64 time_accumulated_on_this_test; + u64 start_time; + u64 run_duration; +} RepTester; + +typedef struct ReadArgs { + Buffer dest; + Str8 file_name; +} ReadArgs; + +void RepTester_Error(RepTester *tester, Str8 msg) { + tester->mode = RepTesterMode_Error; + fprintf(stderr, "ERROR: %.*s\n", STR8_FMT(msg)); +} + +void RepTester_BeginTime(RepTester *tester) +{ + tester->open_block_count++; + tester->time_accumulated_on_this_test -= ReadCPUTimer(); +} + +void RepTester_EndTime(RepTester *tester) +{ + tester->close_block_count++; + tester->time_accumulated_on_this_test += ReadCPUTimer(); +} + +void RepTester_CountBytes(RepTester *tester, size_t bytes_read) +{ + tester->total_bytes_read += bytes_read; +} + +static void PrintTime(Str8 label, f64 cpu_time, u64 cpu_timer_freq, u64 byte_count) +{ + printf("%.*s: %.0f", STR8_FMT(label), cpu_time); + if (cpu_timer_freq) { + f64 seconds = cpu_time / CAST(f64)cpu_timer_freq; + printf(" (%fms)", 1000.0f * seconds); + + if (byte_count) { + f64 gigabyte = (1024.0f * 1024.0f * 1024.0f); + f64 best_bandwidth = byte_count / (gigabyte * seconds); + printf(" %fgb/s", best_bandwidth); + } + } +} + +static void RepTester_PrintResult(RepTesterResults results, u64 cpu_timer_freq, u64 byte_count) +{ + PrintTime(STR8("Min"), (f64)results.min_time, cpu_timer_freq, byte_count); + printf("\n"); + + PrintTime(STR8("Max"), (f64)results.max_time, cpu_timer_freq, byte_count); + printf("\n"); + + if (results.test_count) { + PrintTime(STR8("Avg"), (f64)results.total_time / (f64)results.test_count, cpu_timer_freq, byte_count); + printf("\n"); + } +} + +bool RepTester_IsTesting(RepTester *tester) +{ + if (tester->mode == RepTesterMode_Testing) { + u64 current_time = ReadCPUTimer(); + if (tester->open_block_count) { + if (tester->open_block_count != tester->close_block_count) + RepTester_Error(tester, STR8("Unbalanced begin/end time")); + + if (tester->total_bytes_read != tester->desired_bytes_read) + RepTester_Error(tester, STR8("Processed byte count mismatch")); + + if (tester->mode == RepTesterMode_Testing) { + RepTesterResults *results = &tester->results; + u64 elapsed_time = tester->time_accumulated_on_this_test; + results->test_count += 1; + results->total_time += elapsed_time; + results->max_time = MAX(results->max_time, elapsed_time); + + if (results->min_time > elapsed_time) { + results->min_time = elapsed_time; + + // NOTE: Reset the trial time when new min time is achieved + tester->start_time = current_time; + PrintTime(STR8("Min"), + (f64)results->min_time, + tester->cpu_timer_freq, + tester->desired_bytes_read); + printf(" \r"); + } + + tester->open_block_count = 0; + tester->close_block_count = 0; + tester->time_accumulated_on_this_test = 0; + tester->total_bytes_read = 0; + } + } + + if ((current_time - tester->start_time) > tester->run_duration) { + tester->mode = RepTesterMode_Complete; + + printf(" \r"); + RepTester_PrintResult(tester->results, tester->cpu_timer_freq, tester->desired_bytes_read); + } + } + + bool result = tester->mode == RepTesterMode_Testing; + return result; +} + +static void ReadWithFRead(RepTester *tester, ReadArgs *args) +{ + while (RepTester_IsTesting(tester)) { + FILE *file = fopen(args->file_name.data, "rb"); + if (file) { + RepTester_BeginTime(tester); + size_t result = fread(args->dest.data, args->dest.size, 1, file); + RepTester_EndTime(tester); + + if (result == 1) { + RepTester_CountBytes(tester, args->dest.size); + } else { + RepTester_Error(tester, STR8("fopen failed")); + } + fclose(file); + } else { + RepTester_Error(tester, STR8("fopen failed")); + } + } +} + +static void ReadWithRead(RepTester *tester, ReadArgs *args) +{ + while (RepTester_IsTesting(tester)) { + int file = _open(args->file_name.data, _O_BINARY | _O_RDONLY); + if (file != -1) { + char *dest = args->dest.data; + u64 space_remaining = args->dest.size; + while (space_remaining) { + u32 read_size = UINT32_MAX; + if ((u64)read_size > space_remaining) + read_size = (u32)space_remaining; + + RepTester_BeginTime(tester); + int result = _read(file, dest, read_size); + RepTester_EndTime(tester); + + if (result == (int)read_size) { + RepTester_CountBytes(tester, read_size); + } else { + RepTester_Error(tester, STR8("_read failed")); + break; + } + + space_remaining -= read_size; + dest += read_size; + } + _close(file); + } else { + RepTester_Error(tester, STR8("_open failed")); + } + } +} + +static void ReadWithReadFile(RepTester *tester, ReadArgs *args) +{ + while (RepTester_IsTesting(tester)) { + HANDLE File = CreateFileA(args->file_name.data, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); + if (File != INVALID_HANDLE_VALUE) { + char *dest = args->dest.data; + u64 space_remaining = args->dest.size; + while (space_remaining) { + u32 read_size = UINT32_MAX; + if ((u64)read_size > space_remaining) + read_size = (u32)space_remaining; + + DWORD bytes_read = 0; + RepTester_BeginTime(tester); + BOOL result = ReadFile(File, dest, read_size, &bytes_read, 0); + RepTester_EndTime(tester); + + if (result && (bytes_read == read_size)) + RepTester_CountBytes(tester, read_size); + else + RepTester_Error(tester, STR8("ReadFile failed")); + + space_remaining -= read_size; + dest += read_size; + } + + CloseHandle(File); + } else { + RepTester_Error(tester, STR8("CreateFileA failed")); + } + } +} + +typedef void TestFuncPtr(RepTester *tester, ReadArgs *args); +typedef struct TestFunction { + Str8 name; + TestFuncPtr *func; +} TestFunction; + +int main(int argc, char const **argv) +{ + if (argc != 2) + fprintf(stderr, "Usage: %s [existing filename]\n", argv[0]); + + Str8 file_name = {.data = CAST(char *) argv[0], .size = strlen(argv[0])}; + struct __stat64 stat; + _stat64(file_name.data, &stat); + + ReadArgs args = {}; + args.dest.data = VirtualAlloc(NULL, stat.st_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + args.dest.size = stat.st_size; + args.file_name = file_name; + + if (stat.st_size <= 0) { + fprintf(stderr, "ERROR: Test data size must be non-zero\n"); + return -1; + } + + if (!args.dest.data) { + fprintf(stderr, "ERROR: Failed to allocate %zu bytes\n", stat.st_size); + return -1; + } + + TestFunction test_functions[] = { + {STR8("fread"), ReadWithFRead}, + {STR8("_read"), ReadWithRead}, + {STR8("ReadFile"), ReadWithReadFile}, + }; + + RepTester testers[ARRAY_UCOUNT(test_functions)] = {}; + u64 cpu_timer_freq = EstimateCPUTimerFreq(); + for (u64 index = 0; index != UINT64_MAX; index++) { + for (u32 func_index = 0; func_index < ARRAY_UCOUNT(testers); func_index++) { + RepTester *tester = testers + func_index; + TestFunction test_func = test_functions[func_index]; + + printf("\n--- %.*s ---\n", STR8_FMT(test_func.name)); + + if (tester->mode == RepTesterMode_Nil) { + tester->mode = RepTesterMode_Testing; + tester->desired_bytes_read = args.dest.size; + tester->cpu_timer_freq = cpu_timer_freq; + tester->results.min_time = (u64)-1; + } else if (tester->mode == RepTesterMode_Complete) { + tester->mode = RepTesterMode_Testing; + + if (tester->desired_bytes_read != args.dest.size) + RepTester_Error(tester, STR8("desired_bytes_read changed")); + if (tester->cpu_timer_freq != cpu_timer_freq) + RepTester_Error(tester, STR8("cpu frequency changed")); + } + + tester->run_duration = /*seconds_to_try*/ 10 * cpu_timer_freq; + tester->start_time = ReadCPUTimer(); + + test_func.func(tester, &args); + } + } + return 0; } diff --git a/project.rdbg b/project.rdbg index 9f4fff7ece405d0d0d5ebfb0d0db71cb9f06392a..624a31f1ebeb7f113e23f816cd742be50182326c 100644 GIT binary patch delta 153 zcmZ3^xsh|i78V8|*j&n3$|TAJWQ75-vsH{oer{@vb54F~%0zx0Ipdh3)PmHK%#zIf zy!evT;*!*&_>|P7(saGlid4OcfreZlL%@J>@_Xh~Zjecgj2u9cb#fw$sTPm{65<46 ckHoUnqT