gmio/benchmarks/commons/benchmark_tools.c

413 lines
13 KiB
C
Raw Normal View History

2015-09-01 23:01:38 +08:00
/****************************************************************************
** gmio benchmarks
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software provides performance benchmarks for the gmio library
** (https://github.com/fougue/gmio)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
2015-09-01 23:30:49 +08:00
#include "benchmark_tools.h"
2015-09-01 23:01:38 +08:00
2015-10-28 23:03:31 +08:00
#include <float.h>
#include <stdarg.h>
2015-09-01 23:01:38 +08:00
#include <stdio.h>
#include <string.h>
2015-09-01 23:01:38 +08:00
#if WIN32
2015-09-01 23:01:38 +08:00
# include <windows.h>
2015-09-01 23:30:49 +08:00
# define BENCHMARK_TIMER_WINDOWS
2015-09-01 23:01:38 +08:00
#else
# include <time.h>
2015-09-01 23:30:49 +08:00
# define BENCHMARK_TIMER_LIBC
2015-09-01 23:01:38 +08:00
#endif
/* Benchmark timers */
struct benchmark_timer
2015-09-01 23:01:38 +08:00
{
2015-09-01 23:30:49 +08:00
#ifdef BENCHMARK_TIMER_WINDOWS
2015-09-01 23:01:38 +08:00
LARGE_INTEGER start_time;
LARGE_INTEGER frequency;
2015-09-01 23:30:49 +08:00
#elif defined(BENCHMARK_TIMER_LIBC)
2015-09-01 23:01:38 +08:00
clock_t start_tick;
#endif
};
2015-09-01 23:01:38 +08:00
static void benchmark_timer_start(struct benchmark_timer* timer)
2015-09-01 23:01:38 +08:00
{
2015-09-01 23:30:49 +08:00
#ifdef BENCHMARK_TIMER_WINDOWS
2015-09-01 23:01:38 +08:00
QueryPerformanceFrequency(&timer->frequency);
QueryPerformanceCounter(&timer->start_time);
2015-09-01 23:30:49 +08:00
#elif defined(BENCHMARK_TIMER_LIBC)
2015-09-01 23:01:38 +08:00
timer->start_tick = clock();
#endif
}
static gmio_time_ms_t benchmark_timer_elapsed_ms(const struct benchmark_timer* timer)
2015-09-01 23:01:38 +08:00
{
2015-09-01 23:30:49 +08:00
#ifdef BENCHMARK_TIMER_WINDOWS
LARGE_INTEGER end_time = {0};
LARGE_INTEGER elapsed = {0};
2015-09-01 23:01:38 +08:00
QueryPerformanceCounter(&end_time);
elapsed.QuadPart = end_time.QuadPart - timer->start_time.QuadPart;
/*
We now have the elapsed number of ticks, along with the
number of ticks-per-second. We use these values
to convert to the number of elapsed microseconds.
To guard against loss-of-precision, we convert
to milliseconds *before* dividing by ticks-per-second.
*/
elapsed.QuadPart *= 1000;
elapsed.QuadPart /= timer->frequency.QuadPart;
return elapsed.QuadPart;
2015-09-01 23:30:49 +08:00
#elif defined(BENCHMARK_TIMER_LIBC)
2015-09-24 01:32:43 +08:00
const clock_t elapsed_ticks = clock() - timer->start_tick;
const double elapsed_ms = (elapsed_ticks * 1000) / ((double)CLOCKS_PER_SEC);
return (gmio_time_ms_t)elapsed_ms;
2015-09-01 23:01:38 +08:00
#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)
fputs(str, stdout);
}
/*! 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 */
2015-10-28 23:03:31 +08:00
static const char unit_time_str[] = "ms";
2015-10-28 23:03:31 +08:00
/*! String representation of some data not available */
static const char n_a[] = "N/A";
/*! Generic formatted print of a string */
static void gprintf_func_string(
/* 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,
2015-10-28 23:03:31 +08:00
/* String to be printed */
const char* str)
{
if (width_column > 0)
func_gprintf(cookie, "%-*s", width_column, str);
else
func_gprintf(cookie, "%s", str);
}
/*! Generic formatted print of some execution time */
static void gprintf_func_exec_time(
void* cookie,
func_gprintf_t func_gprintf,
size_t width_column,
gmio_time_ms_t 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, unit_time_str);
2015-10-28 23:03:31 +08:00
gprintf_func_string(cookie, func_gprintf, width_column, str_time);
}
else {
2015-10-28 23:03:31 +08:00
gprintf_func_string(cookie, func_gprintf, width_column, n_a);
}
}
/*! Generic formatted print of a ratio */
static void gprintf_func_exec_ratio(
void* cookie,
func_gprintf_t func_gprintf,
size_t width_column,
float ratio)
{
if (!(ratio < 0)) { /* Valid ratio */
char str_ratio[128] = {0};
sprintf(str_ratio, "%.2f", ratio);
gprintf_func_string(cookie, func_gprintf, width_column, str_ratio);
}
else {
gprintf_func_string(cookie, func_gprintf, width_column, n_a);
}
}
/*! Helper for printf() around gprintf_func_exec_time() */
static void printf_func_exec_time(
size_t width_column,
gmio_time_ms_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(struct benchmark_cmp_result_array 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 struct benchmark_cmp_result* cmp,
gmio_time_ms_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 struct benchmark_cmp_result* cmp,
gmio_time_ms_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 struct benchmark_cmp_result*, gmio_time_ms_t*, gmio_bool_t*);
/*! Returns the strlen of the longest execution time string */
static size_t find_maxlen_cmp_result_func_exec_time(
struct benchmark_cmp_result_array 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) {
gmio_time_ms_t time = 0;
gmio_bool_t has_time = GMIO_FALSE;
func_select_exec_infos(&res_array.ptr[i], &time, &has_time);
gprintf_func_exec_time(strbuff, &sprintf_wrap, 0, time, has_time);
2015-10-28 23:03:31 +08:00
max_len = size_t_max(safe_strlen(strbuff), max_len);
}
return max_len;
}
2015-10-28 23:03:31 +08:00
/*! Returns the strlen of the longest func2/func1 ratio string */
static size_t find_maxlen_cmp_result_ratio(
struct benchmark_cmp_result_array res_array)
2015-10-28 23:03:31 +08:00
{
char strbuff[1024] = {0};
size_t max_len = 0;
size_t i;
for (i = 0; i < res_array.count; ++i) {
const float ratio = res_array.ptr[i].func2_func1_ratio;
gprintf_func_exec_ratio(strbuff, &sprintf_wrap, 0, ratio);
max_len = size_t_max(safe_strlen(strbuff), max_len);
}
return max_len;
}
static void update_benchmark_cmp_result_ratio(
struct benchmark_cmp_result* result)
2015-10-28 23:03:31 +08:00
{
if (result->has_func1_exec_time && result->has_func2_exec_time) {
if (result->func2_exec_time_ms > 0) {
result->func2_func1_ratio =
result->func2_exec_time_ms
/ (float)result->func1_exec_time_ms;
}
else {
result->func2_func1_ratio = FLT_MAX;
}
}
else {
result->func2_func1_ratio = -1.f; /* Non-valid ratio */
}
}
/* Implementation */
struct benchmark_cmp_result benchmark_cmp(struct benchmark_cmp_arg arg)
2015-09-01 23:01:38 +08:00
{
struct benchmark_cmp_result result = {0};
result.tag = arg.tag;
2015-09-01 23:01:38 +08:00
if (arg.func1 != NULL) {
struct benchmark_timer timer = {0};
benchmark_timer_start(&timer);
(*arg.func1)(arg.func1_arg);
result.func1_exec_time_ms = benchmark_timer_elapsed_ms(&timer);
result.has_func1_exec_time = GMIO_TRUE;
}
if (arg.func2 != NULL) {
struct benchmark_timer timer = {0};
benchmark_timer_start(&timer);
(*arg.func2)(arg.func2_arg);
result.func2_exec_time_ms = benchmark_timer_elapsed_ms(&timer);
result.has_func2_exec_time = GMIO_TRUE;
}
2015-10-28 23:03:31 +08:00
update_benchmark_cmp_result_ratio(&result);
2015-09-01 23:01:38 +08:00
return result;
}
2015-09-01 23:01:38 +08:00
2015-10-28 23:03:31 +08:00
void benchmark_cmp_batch(
size_t run_count,
const struct benchmark_cmp_arg *arg_array,
struct benchmark_cmp_result *result_array,
2015-10-28 23:03:31 +08:00
void (*func_init)(),
void (*func_cleanup)())
{
size_t run; /* for-loop index */
size_t array_size = 0;
while (arg_array[array_size].tag != NULL)
++array_size;
for (run = 0; run < run_count; ++run) {
size_t i; /* for-loop index */
/* Init */
if (func_init)
(*func_init)();
for (i = 0; i < array_size; ++i) {
const struct benchmark_cmp_result ires = benchmark_cmp(arg_array[i]);
struct benchmark_cmp_result* fres = &result_array[i];
2015-10-28 23:03:31 +08:00
if (run != 0) {
if (fres->func1_exec_time_ms > ires.func1_exec_time_ms)
fres->func1_exec_time_ms = ires.func1_exec_time_ms;
if (fres->func2_exec_time_ms > ires.func2_exec_time_ms)
fres->func2_exec_time_ms = ires.func2_exec_time_ms;
update_benchmark_cmp_result_ratio(fres);
}
else {
*fres = ires;
}
}
/* Cleanup */
if (func_cleanup)
(*func_cleanup)();
}
}
void benchmark_print_results(
enum benchmark_print_format format,
struct benchmark_cmp_result_header header,
struct benchmark_cmp_result_array result_array)
2015-09-01 23:01:38 +08:00
{
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 : "";
2015-10-28 23:03:31 +08:00
const char* header_ratio = "";
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));
2015-10-28 23:03:31 +08:00
const size_t width_ratio_col =
size_t_max(
find_maxlen_cmp_result_ratio(result_array),
safe_strlen(header_ratio));
size_t i; /* for-loop index*/
/* Print table header */
printf("%*s | ", (int)width_tag_col, "");
printf("%-*s | ", (int)width_func1_col, header_comp1);
2015-10-28 23:03:31 +08:00
printf("%-*s | ", (int)width_func2_col, header_comp2);
printf("%-*s\n", (int)width_ratio_col, header_ratio);
/* Print separation between header and results */
print_string_n("-", width_tag_col + 1);
printf("|");
print_string_n("-", width_func1_col + 2);
printf("|");
print_string_n("-", width_func2_col + 2);
2015-10-28 23:03:31 +08:00
printf("|");
print_string_n("-", width_ratio_col + 2);
printf("\n");
/* Print benchmark result lines */
for (i = 0; i < result_array.count; ++i) {
const struct benchmark_cmp_result result = result_array.ptr[i];
printf("%-*s | ", (int)width_tag_col, result.tag);
printf_func_exec_time(
width_func1_col,
result.func1_exec_time_ms,
result.has_func1_exec_time);
printf(" | ");
printf_func_exec_time(
width_func2_col,
result.func2_exec_time_ms,
result.has_func2_exec_time);
2015-10-28 23:03:31 +08:00
printf(" | ");
gprintf_func_exec_ratio(
NULL, printf_wrap,
width_ratio_col, result.func2_func1_ratio);
printf("\n");
}
2015-09-01 23:01:38 +08:00
}
}