mirror of
https://github.com/rizinorg/rizin
synced 2026-08-22 20:26:16 -04:00
* Add option to replace (geometric) invalid values with another value. * Add geometric Mean and Std Dev to benchmarks. * Add Doxygen documentation * Add a README to the bench dir as intro.
135 lines
4 KiB
C
135 lines
4 KiB
C
// SPDX-FileCopyrightText: 2025 Anton Angelov <anton.angelov@protonmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#ifndef BENCH_UTILS_H
|
|
#define BENCH_UTILS_H
|
|
|
|
#include <rz_types.h>
|
|
#include <rz_util.h>
|
|
|
|
/**
|
|
* \brief Description of the state of a micro benchmark
|
|
*/
|
|
typedef struct rz_bench_ctx_t {
|
|
const char *name; ///< name of a micro benchmark
|
|
ut64 iterations; ///< number of iterations
|
|
ut64 start_time; ///< start time of the benchmark in microseconds
|
|
ut64 total_time; ///< total elapsed time of the benchmark in microseconds
|
|
double arith_mean_us; ///< The average time needed per iteration in microseconds (arithmetic mean).
|
|
double arith_std_dev; ///< Arithmetic standard deviation of benchmark samples in microseconds.
|
|
double geo_mean_us; ///< The average time needed per iteration in microseconds (geometric mean).
|
|
double geo_std_dev; ///< Geometric standard deviation of benchmark samples.
|
|
} RzBenchCtx;
|
|
|
|
RZ_API void rz_bench_init(RZ_NONNULL RzBenchCtx *ctx, RZ_NONNULL const char *name, ut64 iterations);
|
|
RZ_API void rz_bench_start(RZ_NONNULL RzBenchCtx *ctx);
|
|
RZ_API void rz_bench_end(RZ_NONNULL RzBenchCtx *ctx);
|
|
RZ_API void rz_bench_report(RZ_NONNULL RzBenchCtx *ctx, RZ_NONNULL RzTable *t);
|
|
|
|
/**
|
|
* \brief The variable to replace data points == 0.0 with.
|
|
* The benchmark has microseconds as data points.
|
|
* So 0.1 microsecond as replacement for a non-measurable
|
|
* difference in runtime seems fine.
|
|
*/
|
|
#define RZ_BENCH_INVALID_GEO_VAR_REPLACEMENT 0.1
|
|
|
|
|
|
/**
|
|
* \brief Run a benchmark with the given code block
|
|
*
|
|
* Example usage:
|
|
* \code
|
|
* RZ_BENCH_RUN_I("my_function", cnt, table, 1000000, {
|
|
* my_function(data);
|
|
* });
|
|
* \endcode
|
|
*/
|
|
#define RZ_BENCH_RUN_I(name, i, table, iterations, code) \
|
|
do { \
|
|
RzMathWelfordSums wf; \
|
|
rz_math_welford_init(&wf, RZ_BENCH_INVALID_GEO_VAR_REPLACEMENT); \
|
|
RzBenchCtx ctx; \
|
|
rz_bench_init(&ctx, name, iterations); \
|
|
rz_bench_start(&ctx); \
|
|
for (ut64(i) = 0; (i) < iterations; (i)++) { \
|
|
ut64 spl = rz_time_now_mono(); \
|
|
code; \
|
|
rz_math_welford_push(&wf, (double)(rz_time_now_mono() - spl)); \
|
|
} \
|
|
rz_bench_end(&ctx); \
|
|
ctx.arith_std_dev = rz_math_welford_astddev(&wf); \
|
|
ctx.arith_mean_us = rz_math_welford_amean(&wf); \
|
|
ctx.geo_std_dev = rz_math_welford_gstddev(&wf); \
|
|
ctx.geo_mean_us = rz_math_welford_gmean(&wf); \
|
|
rz_bench_report(&ctx, table); \
|
|
} while (0)
|
|
|
|
/**
|
|
* \brief Run a benchmark with the given code block
|
|
*
|
|
* Example usage:
|
|
* \code
|
|
* RZ_BENCH_RUN("my_function", table, 1000000, {
|
|
* my_function(data);
|
|
* });
|
|
* \endcode
|
|
*/
|
|
#define RZ_BENCH_RUN(name, table, iterations, code) \
|
|
RZ_BENCH_RUN_I(name, i, table, iterations, code)
|
|
|
|
/**
|
|
* \brief Initializes the RzTable \p T used for storing results of microbenchmarks.
|
|
* \param T table to initialize.
|
|
*/
|
|
#define RZ_BENCH_TABLE_INIT(T) \
|
|
rz_table_set_columnsf(T, "sdnnnnnn", \
|
|
"Benchmark", \
|
|
"| Iterations", \
|
|
"| Total time [ms]", \
|
|
"| Throughput [iterations/sec]", \
|
|
"| Mean [us/iteration]", \
|
|
"| Standard Deviation", \
|
|
"| Mean (geometric)", \
|
|
"| Std. Dev (geometric)");
|
|
|
|
/**
|
|
* \brief Prints microbenchmark results and frees the RzTable \p T. Should be called at end of a benchmark suite.
|
|
* \param T table to print and free.
|
|
*/
|
|
#define RZ_BENCH_TABLE_PRINT_AND_FREE(T) \
|
|
char *table_out = rz_table_tostring(T); \
|
|
printf("%s\n", table_out); \
|
|
free(table_out); \
|
|
rz_table_free(T);
|
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
/**
|
|
* \def Helper macro to prevent compiler optimizations on benchmarked code.
|
|
* \param type Return type of the expression \p x.
|
|
* \param x Function or code to be executed.
|
|
*/
|
|
#define RZ_DONT_OPTIMIZE(type, x) \
|
|
do { \
|
|
type tmp_ = (x); \
|
|
__asm__ volatile("" : : "r,m"(tmp_) : "memory"); \
|
|
} while (0)
|
|
#elif defined(_MSC_VER)
|
|
#include <intrin.h>
|
|
#define RZ_DONT_OPTIMIZE(type, x) \
|
|
do { \
|
|
type tmp_ = (x); \
|
|
_WriteBarrier(); \
|
|
*(volatile char *)&tmp_; \
|
|
_ReadBarrier(); \
|
|
} while (0)
|
|
#else
|
|
// Fallback
|
|
#define RZ_DONT_OPTIMIZE(type, x) \
|
|
do { \
|
|
volatile type tmp_ = (x); \
|
|
(void)tmp_; \
|
|
} while (0)
|
|
#endif
|
|
|
|
#endif // BENCH_UTILS_H
|