/**
 * @file include/benchmarking.h
 * @brief Define macros for benchmarking
 */

#pragma once
#ifdef BENCHMARK_MODE
 #include "ansiesc.h"
 #include "def.h"
 #include <stdio.h>
 #include <time.h>

 #ifndef REPEAT
  #define REPEAT 10'000
 #endif

 #define CADR(...) CAR(CDR(__VA_ARGS__))

 #define BENCH_HEADER " ■ " ESCBLU "Benchmarking " ESCLR

 #define BENCH_TEMPLATE(name, identifier, time_fn_pair) \
   static void bench__bench##identifier(); \
   [[gnu::constructor]] static void bench__run##identifier() { \
     printf(BENCH_HEADER ESBLD #name ESCLR "..."); \
     double begin = (double)CAR time_fn_pair; \
     for (int i = 0; i < REPEAT; i++) [[clang::always_inline]] \
       bench__bench##identifier(); \
     double val = (double)CAR time_fn_pair - begin; \
     printf(" => %.6f " CADR time_fn_pair "\n", val / REPEAT); \
   } \
   static void bench__bench##identifier()

 #define bench(name) BENCH_TEMPLATE(name, name##bench, (clock(), "microsecs"))

 #define bench_cycle(name) \
   BENCH_TEMPLATE( \
     name, name##benchcycle, (__builtin_readcyclecounter(), "cycle") \
   )

 #define main bench__dummymain
#else
// --gc-sections
 #define bench(a)       [[gnu::unused]] static void BENCH_dum##a()
 #define bench_cycle(a) [[gnu::unused]] static void BENCH_dumc##a()
#endif