33#include  < type_traits> 
44#include  < utility> 
55#include  < string> 
6- 
6+ #include  < variant> 
7+ #include  < vector> 
8+ #include  < algorithm> 
9+ #include  < numeric> 
710
811template  <class  T >
912struct  return_type_holder 
@@ -14,8 +17,7 @@ struct return_type_holder
1417template  <>
1518struct  return_type_holder <void >
1619{
17-  struct  empty_struct  {};
18-  using  type = empty_struct;
20+  using  type = std::monostate;
1921};
2022
2123/* 
@@ -40,40 +42,100 @@ struct return_type_holder<void>
4042 std::cout << "result: " << std::to_string(result) << "\n"; 
4143 }); 
4244*/ 
45+ 
46+ 
47+ 
48+ 
4349struct  benchmarker 
4450{
4551 using  clock = std::chrono::high_resolution_clock;
4652 using  duration = std::chrono::duration<double , std::micro>;
4753 using  duration_type = duration::rep;
54+  constexpr  static  inline  size_t  maximum_sample_size = 100 ;
4855
4956 template  <typename  Fun, typename  ...Args>
5057 static  inline  auto  run (Fun&& func, Args&& ...args) -> std::pair<duration_type, typename return_type_holder<decltype(std::forward<Fun>(func)(std::forward<Args>(args)...))>::type>
5158 {
52-  using  return_type = std::invoke_result_t <Fun, Args...>;
53-  // using return_type = decltype(std::forward<Fun>(func)(std::forward<Args>(args)...));
54-  duration elapsed;
55-  clock::time_point start = clock::now ();
59+  using  func_return_type = std::invoke_result_t <Fun, Args...>;
60+  double  total_duration{}, mean{}, total_stdev{};
61+  std::vector<double > elapsed_times;
62+  std::vector<double > stdev_list;
63+  size_t  sample_size = 0 ;
64+  duration elapsed{};
65+  elapsed_times.reserve (benchmarker::maximum_sample_size);
66+  stdev_list.reserve (benchmarker::maximum_sample_size);
67+  auto  calc_stdev = [] (const  double  mean, const  std::vector<double >& samples) -> double 
68+  {
69+  const  auto  num_samples = samples.size ();
70+  std::vector<double > variance_mean (num_samples);
71+  std::transform (begin (samples), end (samples), begin (variance_mean), [mean](double  e) { return  e - mean; });
72+  const  double  sq_sum = std::inner_product (begin (variance_mean), end (variance_mean), begin (variance_mean), 0.0 );
73+  const  double  stdev = std::sqrt (sq_sum / num_samples);
74+  return  stdev;
75+  };
5676
57-  if  constexpr  (std::is_void_v<return_type>)
77+  using  ret_type = std::variant<typename  return_type_holder<func_return_type>::type, std::monostate>;
78+  ret_type invoke_result_variant;
79+  do 
5880 {
59-  std::invoke (std::forward<Fun>(func), std::forward<Args>(args)...);
81+  const  auto  start = clock::now ();
82+  invoke_result_variant = benchmarker::invoke_func (std::forward<Fun>(func), std::forward<Args>(args)...);
6083 elapsed = clock::now () - start;
61-  return  std::make_pair (elapsed.count () / 1000.0 , return_type_holder<void >::empty_struct{});
84+  const  auto  elapsed_time = elapsed.count ();
85+  elapsed_times.push_back (elapsed_time);
86+  total_duration += elapsed_time;
87+  mean = total_duration / elapsed_times.size ();
88+ 
89+  auto  stdev_1 = calc_stdev (mean, elapsed_times);
90+  total_stdev += stdev_1;
91+  stdev_list.push_back (stdev_1);
92+ 
93+  const  auto  stdev_mean = total_stdev / stdev_list.size ();
94+  const  auto  stdev = calc_stdev (stdev_mean, stdev_list);
95+ 
96+  if  (stdev > 0  && (stdev / mean) < 0.03 ) {
97+  break ; //  warm-up state is done
98+  }
99+  } while  ((++sample_size <= benchmarker::maximum_sample_size) &&
100+  (elapsed <= std::chrono::milliseconds (823 )));
101+ 
102+  auto  [mmin, mmax] = std::minmax_element (std::begin (elapsed_times), std::end (elapsed_times));
103+  std::cout << std::fixed
104+  << " benchmark results >>> " 
105+  << " total samples: " size ()
106+  << " | mean: " 
107+  << " | min: " 
108+  << " | max: " "  us\n " 
109+ 
110+  return  std::make_pair (mean / 1000.0 , std::get<func_return_type>(invoke_result_variant));
111+  }
112+ 
113+ private: 
114+ 
115+  template  <typename  Fun, typename  ...Args>
116+  [[nodiscard]] static  auto  invoke_func (Fun&& func, Args&& ...args) -> std::variant<typename return_type_holder<std::invoke_result_t<Fun, Args...>>::type, std::monostate>
117+  {
118+  using  func_return_type = std::invoke_result_t <Fun, Args...>;
119+  using  ret_type = std::variant<typename  return_type_holder<std::invoke_result_t <Fun, Args...>>::type, std::monostate>;
120+  if  constexpr  (std::is_void_v<func_return_type>)
121+  {
122+  std::invoke (std::forward<Fun>(func), std::forward<Args>(args)...);
123+  return  ret_type{std::monostate{}};
62124 }
63125 else 
64126 {
65-  return_type res = std::forward<Fun>(func)(std::forward<Args>(args)...);
66-  elapsed = clock::now () - start;
67-  return  std::make_pair (elapsed.count () / 1000.0 , std::move (res));
127+  func_return_type res = std::invoke (std::forward<Fun>(func), std::forward<Args>(args)...);
128+  return  ret_type{res};
68129 }
69130 }
70131
132+ 
71133};
72134
73135#ifdef  _WIN32
74136//  to_string overload for void types 
75137_STD_BEGIN
76- _NODISCARD inline  string to_string (return_type_holder< void >::empty_struct )
138+ _NODISCARD inline  string to_string (std::monostate )
77139{//  convert void to string
78140 return  string{};
79141}
0 commit comments