From 5d514218ac195f48b759bf16a96fa165bcc57ede Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Wed, 28 Oct 2015 14:56:52 +0100 Subject: [PATCH] benchmarks: let size of table columns adapts to contents --- benchmarks/commons/benchmark_tools.c | 216 ++++++++++++++++++++++----- benchmarks/commons/benchmark_tools.h | 18 +++ 2 files changed, 198 insertions(+), 36 deletions(-) diff --git a/benchmarks/commons/benchmark_tools.c b/benchmarks/commons/benchmark_tools.c index e911395..6d0522d 100644 --- a/benchmarks/commons/benchmark_tools.c +++ b/benchmarks/commons/benchmark_tools.c @@ -15,6 +15,7 @@ #include "benchmark_tools.h" +#include #include #include @@ -26,7 +27,9 @@ # define BENCHMARK_TIMER_LIBC #endif -typedef struct benchmark_timer +/* Benchmark timers */ + +struct benchmark_timer { #ifdef BENCHMARK_TIMER_WINDOWS LARGE_INTEGER start_time; @@ -34,7 +37,8 @@ typedef struct benchmark_timer #elif defined(BENCHMARK_TIMER_LIBC) clock_t start_tick; #endif -} benchmark_timer_t; +}; +typedef struct benchmark_timer benchmark_timer_t; static void benchmark_timer_start(benchmark_timer_t* timer) { @@ -72,6 +76,151 @@ static int64_t benchmark_timer_elapsed_ms(const benchmark_timer_t* timer) #endif } +/* Wraps around formatted printing functions */ + +/*! Wrapp around sprintf() to be used with gprintf_func_exec_time() */ +static void sprintf_wrap(void* cookie, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsprintf((char*)cookie, fmt, args); + va_end(args); +} + +/*! Wrap around printf() to be used with gprintf_func_exec_time() */ +static void printf_wrap(void* cookie, const char* fmt, ...) +{ + va_list args; + GMIO_UNUSED(cookie); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +/*! Typedef on pointer to printf-wrap functions(eg. sprintf_wrap()) */ +typedef void (*func_gprintf_t)(void*, const char*, ...); + + +/* Utilities */ + +/*! Calls printf(str) \p n times */ +static void print_string_n(const char* str, size_t n) +{ + size_t i; /* for-loop index*/ + for (i = 0; i < n; ++i) + printf(str); +} + +/*! Safe wrapper around strlen() for NULL strings */ +GMIO_INLINE size_t safe_strlen(const char* str) +{ + return str != NULL ? strlen(str) : 0; +} + +/*! Returns the maximum of two size_t values */ +GMIO_INLINE size_t size_t_max(size_t lhs, size_t rhs) +{ + return lhs > rhs ? lhs : rhs; +} + +/*! String representation of the unit used for execution time */ +static const char* unit_time_str = "ms"; + +/*! Generic formatted print of some execution time */ +static void gprintf_func_exec_time( + /* Annex data for func_gprintf (ex: char* for sprintf()) */ + void* cookie, + /* Function ptr on a printf wrap (ex: sprintf_wrap()) */ + func_gprintf_t func_gprintf, + /* Width of the print column, if any (can be == 0) */ + size_t width_column, + /* Execution time, in ms */ + size_t time_ms, + /* Valid execution time ? */ + gmio_bool_t has_time) +{ + if (has_time) { + char str_time[128] = {0}; + /* TODO: %ull is not accepted by mingw, find a fix(maybe %ul64) */ + sprintf(str_time, "%u%s", (unsigned)time_ms, unit_time_str); + if (width_column > 0) + func_gprintf(cookie, "%-*s", width_column, str_time); + else + func_gprintf(cookie, "%s", str_time); + } + else { + const char n_a[] = "N/A"; + if (width_column > 0) + func_gprintf(cookie, "%-*s", width_column, n_a); + else + func_gprintf(cookie, "%s", n_a); + } +} + +/*! Helper for printf() around gprintf_func_exec_time() */ +static void printf_func_exec_time( + size_t width_column, + size_t time_ms, + gmio_bool_t has_time) +{ + gprintf_func_exec_time( + NULL, &printf_wrap, width_column, time_ms, has_time); +} + +/*! Returns the strlen of the longest tag string */ +static size_t find_maxlen_cmp_result_tag(benchmark_cmp_result_array_t res_array) +{ + size_t max_len = 0; + size_t i; + for (i = 0; i < res_array.count; ++i) { + const size_t len = safe_strlen(res_array.ptr[i].tag); + max_len = size_t_max(len, max_len); + } + return max_len; +} + +/*! Writes in output args the func1 execution informations */ +static void select_cmp_result_func1_exec_infos( + const benchmark_cmp_result_t* cmp, size_t* time, gmio_bool_t* has_time) +{ + *time = cmp->func1_exec_time_ms; + *has_time = cmp->has_func1_exec_time; +} + +/*! Writes in output args the func2 execution informations */ +static void select_cmp_result_func2_exec_infos( + const benchmark_cmp_result_t* cmp, size_t* time, gmio_bool_t* has_time) +{ + *time = cmp->func2_exec_time_ms; + *has_time = cmp->has_func2_exec_time; +} + +/*! Typedef on pointer to functions like select_cmp_result_funcX_exec_infos() */ +typedef void (*func_select_cmp_result_func_exec_infos_t)( + const benchmark_cmp_result_t*, size_t*, gmio_bool_t*); + +/*! Returns the strlen of the longest execution time string */ +static size_t find_maxlen_cmp_result_func_exec_time( + benchmark_cmp_result_array_t res_array, + func_select_cmp_result_func_exec_infos_t func_select_exec_infos) +{ + char strbuff[1024] = {0}; + size_t max_len = 0; + size_t i; + for (i = 0; i < res_array.count; ++i) { + size_t time = 0; + gmio_bool_t has_time = GMIO_FALSE; + size_t len = 0; + func_select_exec_infos(&res_array.ptr[i], &time, &has_time); + gprintf_func_exec_time(strbuff, &sprintf_wrap, 0, time, has_time); + len = safe_strlen(strbuff); + max_len = size_t_max(len, max_len); + } + return max_len; +} + +/* Implementation */ + benchmark_cmp_result_t benchmark_cmp(benchmark_cmp_arg_t arg) { benchmark_cmp_result_t result = {0}; @@ -95,61 +244,56 @@ benchmark_cmp_result_t benchmark_cmp(benchmark_cmp_arg_t arg) return result; } -static void printf_string_n(const char* str, size_t n) -{ - size_t i; /* for-loop index*/ - for (i = 0; i < n; ++i) - printf(str); -} - -static const int width_tag_col = 20; -static const int width_func_col = 10; -static const char* unit_time_str = "ms"; - -static void printf_func_exec_time( - size_t func_exec_time_ms, gmio_bool_t has_func_exec_time) -{ - if (has_func_exec_time) { - char str_exec_time[128] = {0}; - /* TODO: %ull is not accepted by mingw, find a fix(maybe %ul64) */ - sprintf(str_exec_time, "%u%s", func_exec_time_ms, unit_time_str); - printf("%-*s", width_func_col, str_exec_time); - } - else { - printf("%-*s", width_func_col, "N/A"); - } -} - void benchmark_print_results( benchmark_print_format_t format, benchmark_cmp_result_header_t header, benchmark_cmp_result_array_t result_array) { if (format == BENCHMARK_PRINT_FORMAT_MARKDOWN) { + const char* header_comp1 = + header.component_1 != NULL ? header.component_1 : ""; + const char* header_comp2 = + header.component_2 != NULL ? header.component_2 : ""; + const size_t width_tag_col = + find_maxlen_cmp_result_tag(result_array); + const size_t width_func1_col = + size_t_max( + find_maxlen_cmp_result_func_exec_time( + result_array, &select_cmp_result_func1_exec_infos), + safe_strlen(header_comp1)); + const size_t width_func2_col = + size_t_max( + find_maxlen_cmp_result_func_exec_time( + result_array, &select_cmp_result_func2_exec_infos), + safe_strlen(header_comp2)); size_t i; /* for-loop index*/ /* Print table header */ - printf("%*s | ", width_tag_col, ""); - printf("%-*s | ", width_func_col, header.component_1); - printf("%-*s\n", width_func_col, header.component_2); + printf("%*s | ", (int)width_tag_col, ""); + printf("%-*s | ", (int)width_func1_col, header_comp1); + printf("%-*s\n", (int)width_func2_col, header_comp2); /* Print separation between header and results */ - printf_string_n("-", width_tag_col + 1); + print_string_n("-", width_tag_col + 1); printf("|"); - printf_string_n("-", width_func_col + strlen(unit_time_str)); + print_string_n("-", width_func1_col + 2); printf("|"); - printf_string_n("-", width_func_col + strlen(unit_time_str)); + print_string_n("-", width_func2_col + 2); printf("\n"); /* Print benchmark result lines */ for (i = 0; i < result_array.count; ++i) { const benchmark_cmp_result_t result = result_array.ptr[i]; - printf("%-*s | ", width_tag_col, result.tag); + printf("%-*s | ", (int)width_tag_col, result.tag); printf_func_exec_time( - result.func1_exec_time_ms, result.has_func1_exec_time); + width_func1_col, + result.func1_exec_time_ms, + result.has_func1_exec_time); printf(" | "); printf_func_exec_time( - result.func2_exec_time_ms, result.has_func2_exec_time); + width_func2_col, + result.func2_exec_time_ms, + result.has_func2_exec_time); printf("\n"); } } diff --git a/benchmarks/commons/benchmark_tools.h b/benchmarks/commons/benchmark_tools.h index 8bdacab..9fbed03 100644 --- a/benchmarks/commons/benchmark_tools.h +++ b/benchmarks/commons/benchmark_tools.h @@ -21,32 +21,47 @@ GMIO_C_LINKAGE_BEGIN +/*! Typedef on pointer to function to be benchmarked(execution time) */ typedef void (*benchmark_file_func_t)(const char*); /* benchmark_cmp */ +/*! Describes a comparison benchmark between two functions */ struct benchmark_cmp_arg { + /*! Brief description of the comparison(eg. "Write to file") */ const char* tag; + /*! Pointer to the 1st function */ benchmark_file_func_t func1; + /*! Argument passed to the 1st function on exec */ const char* func1_filepath; + /*! Pointer to the 2nd function */ benchmark_file_func_t func2; + /*! Argument passed to the 2nd function on exec */ const char* func2_filepath; }; typedef struct benchmark_cmp_arg benchmark_cmp_arg_t; +/*! Holds the result of the exec time comparison between two functions */ struct benchmark_cmp_result { + /*! Identifies the comparison */ const char* tag; + /*! Execution time(in ms) of the 1st function */ size_t func1_exec_time_ms; + /*! Is exec time of the 1st function valid ? */ gmio_bool_t has_func1_exec_time; + /*! Execution time(in ms) of the 2nd function */ size_t func2_exec_time_ms; + /*! Is exec time of the 2nd function valid ? */ gmio_bool_t has_func2_exec_time; }; typedef struct benchmark_cmp_result benchmark_cmp_result_t; +/*! Runs func1 then func2 and measures the respective execution time */ benchmark_cmp_result_t benchmark_cmp(benchmark_cmp_arg_t arg); +/*! Runs a batch(array) of comparison benchmarks */ void benchmark_cmp_batch( size_t run_count, const benchmark_cmp_arg_t* arg_array, @@ -63,6 +78,7 @@ enum benchmark_print_format }; typedef enum benchmark_print_format benchmark_print_format_t; +/*! Array of benchmark_cmp_result */ struct benchmark_cmp_result_array { const benchmark_cmp_result_t* ptr; @@ -70,6 +86,7 @@ struct benchmark_cmp_result_array }; typedef struct benchmark_cmp_result_array benchmark_cmp_result_array_t; +/*! Horizontal header labels for benchmark results(by column) */ struct benchmark_cmp_result_header { const char* component_1; @@ -77,6 +94,7 @@ struct benchmark_cmp_result_header }; typedef struct benchmark_cmp_result_header benchmark_cmp_result_header_t; +/*! Prints formatted benchmark results */ void benchmark_print_results( benchmark_print_format_t format, benchmark_cmp_result_header_t header,