Add the repetition tester
This commit is contained in:
		
							parent
							
								
									65efb397ba
								
							
						
					
					
						commit
						4b1e8720e3
					
				| @ -21,6 +21,9 @@ | |||||||
| 
 | 
 | ||||||
| typedef float f32; | typedef float f32; | ||||||
| typedef double f64; | typedef double f64; | ||||||
|  | typedef uint8_t u8; | ||||||
|  | typedef uint16_t u16; | ||||||
|  | typedef uint32_t u32; | ||||||
| typedef uint64_t u64; | typedef uint64_t u64; | ||||||
| 
 | 
 | ||||||
| // NOTE: Globals ///////////////////////////////////////////////////////////////////////////////////
 | // NOTE: Globals ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
| @ -47,7 +50,7 @@ typedef struct Str8BinarySplitResult { | |||||||
|     Str8 rhs; |     Str8 rhs; | ||||||
| } Str8BinarySplitResult; | } 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 | #define STR8_FMT(string) (int)((string).size), (string).data | ||||||
| 
 | 
 | ||||||
| bool                  Str8_Equals(Str8 lhs, Str8 rhs); | bool                  Str8_Equals(Str8 lhs, Str8 rhs); | ||||||
|  | |||||||
| @ -1,12 +1,308 @@ | |||||||
|  | #define _CRT_SECURE_NO_WARNINGS | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <Windows.h> | #include <Windows.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <io.h> | ||||||
|  | #include <sys/stat.h> | ||||||
| 
 | 
 | ||||||
| #include "base.h" | #include "base.h" | ||||||
| #include "listing_0074_platform_metrics.cpp" | #include "listing_0074_platform_metrics.cpp" | ||||||
| #include "base.c" | #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; |     return 0; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								project.rdbg
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								project.rdbg
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user