Skip to content

Commit 6fb96dd

Browse files
author
Oguzhan Katli
committed
benchmarking improved
1 parent d496898 commit 6fb96dd

File tree

2 files changed

+79
-18
lines changed

2 files changed

+79
-18
lines changed

inc/benchmarker.hpp

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
#include <type_traits>
44
#include <utility>
55
#include <string>
6-
6+
#include <variant>
7+
#include <vector>
8+
#include <algorithm>
9+
#include <numeric>
710

811
template <class T>
912
struct return_type_holder
@@ -14,8 +17,7 @@ struct return_type_holder
1417
template <>
1518
struct 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+
4349
struct 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: " << elapsed_times.size()
106+
<< "| mean: " << mean
107+
<< "| min: " << *mmin
108+
<< "| max: " << *mmax << " 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
}

inc/playground_organizer.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ namespace CppOrganizer
230230
class CodeExecuteBenchmark {
231231
public:
232232
template <typename Fun>
233-
explicit CodeExecuteBenchmark(const Fun& func, const ICodeRunnerIdentifier& iden_)
233+
CodeExecuteBenchmark(const Fun& func, const ICodeRunnerIdentifier& iden_)
234234
{
235235
using clock = std::chrono::high_resolution_clock;
236236
using duration = std::chrono::duration<double, std::milli>;
@@ -270,8 +270,7 @@ namespace CppOrganizer
270270
printf("\nCode[%d]:%s failed to execute!\nUnhandled Exception occured!\n", iden_.getID(), iden_.getName().c_str());
271271
}
272272
}
273-
~CodeExecuteBenchmark() {
274-
}
273+
~CodeExecuteBenchmark() = default;
275274
private:
276275
//std::function<void()> f_;
277276
//const ICodeRunnerIdentifier& iden_;
@@ -286,7 +285,7 @@ namespace CppOrganizer
286285
virtual ~CodeRunnerHelper() {};
287286

288287
void RunCode() override {
289-
CodeExecuteBenchmark{ std::bind(&T::Run, static_cast<T *>(this)), *this };
288+
CodeExecuteBenchmark( std::bind(&T::Run, static_cast<T *>(this)), *this );
290289
}
291290

292291
protected:

0 commit comments

Comments
 (0)