Created
September 9, 2017 09:56
-
-
Save stevefan1999-personal/bb70acc0822ef0752aab51cb92dd1362 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <functional> // function | |
#include <type_traits> // is_same | |
#include <future> // extra: async | |
#include <variant> // extra: get, holds_alternative | |
#include <chrono> // high_resolution_clock, high_resolution_clock::time_point, _seconds | |
#include <optional> | |
#include <string> | |
namespace stevefan1999 { | |
using JustInTimerIndependentFn = std::function<void()>; | |
using JustInTimeIgnore = std::function<void(JustInTimerIndependentFn)>; | |
using JustInTimeHighResTime = std::chrono::high_resolution_clock; | |
using JustInTimeHighResTimePoint = std::chrono::high_resolution_clock::time_point; | |
using JustInTimeBestPreciseTime = std::chrono::nanoseconds; | |
using JustInTimeBenchmarkSite = std::function<void()>; | |
using JustInTimeBenchmarkSiteWithIgnorer = std::function<void(JustInTimeIgnore)>; | |
using JustInTimeBenchmarkCandidate = std::variant< // function or function with a "time suspend" function | |
JustInTimeBenchmarkSite, | |
JustInTimeBenchmarkSiteWithIgnorer | |
>; | |
JustInTimeHighResTimePoint JustInTimeGetCurrentTime() { | |
return JustInTimeHighResTime::now(); | |
} | |
struct JustInTimeBenchConfig { | |
friend class JustInTime; | |
bool async = true; | |
std::optional<std::string> name; | |
JustInTimeHighResTimePoint started, finished; | |
private: | |
std::optional<JustInTimeBenchmarkCandidate> benchSite; | |
JustInTimeBestPreciseTime time_ignored = JustInTimeBestPreciseTime::zero(); | |
public: | |
template<class T> | |
constexpr auto getTimeElapsed(); | |
}; | |
using JustInTimeBenchmarkResult = std::vector<JustInTimeBenchConfig>; | |
using JustInTimeOnFinished = std::function<void(JustInTimeBenchConfig &)>; | |
class JustInTime { | |
class JustInTimeDetail { | |
JustInTimeBenchmarkResult m_bench; | |
JustInTimeOnFinished m_asyncBenchFinsihed; | |
JustInTimeOnFinished m_syncBenchFinsihed; | |
private: | |
bool configIsMarshalled() { | |
auto &pop = m_bench.back(); | |
return pop.name && pop.benchSite; | |
} | |
public: // fluent config | |
JustInTimeDetail &addBench(std::string name, JustInTimeBenchmarkCandidate fn, bool async) { | |
if (m_bench.size() > 0 && !configIsMarshalled()) { | |
throw "The previous bench was not properly configurated"; | |
} | |
JustInTimeBenchConfig config; | |
config.name = name; | |
config.benchSite = fn; | |
config.async = async; | |
m_bench.push_back(config); | |
return *this; | |
} | |
auto addBench(std::string name, JustInTimeBenchmarkCandidate fn) { | |
return addBench(name, fn, true); | |
} | |
auto addBench(std::string name) { | |
return [this, name](JustInTimeBenchmarkCandidate fn) { | |
return addBench(name, fn); | |
}; | |
} | |
auto addBench(JustInTimeBenchmarkCandidate fn) { | |
return addBench("<unnamed bench at index " + std::to_string(m_bench.size()) + ">")(fn); | |
} | |
auto addBench() { | |
return addBench({}, {}, true); | |
} | |
JustInTimeDetail &async(bool b = true) { | |
if (m_bench.size() == 0) { | |
throw "no config is pushed"; | |
} | |
m_bench.back().async = b; | |
return *this; | |
} | |
JustInTimeDetail &name(std::string _name) { | |
if (m_bench.size() == 0) { | |
throw "no config is pushed"; | |
} | |
m_bench.back().name = _name; | |
return *this; | |
} | |
JustInTimeDetail &bench(JustInTimeBenchmarkCandidate fn) { | |
if (m_bench.size() == 0) { | |
throw "no config is pushed"; | |
} | |
m_bench.back().benchSite = fn; | |
return *this; | |
} | |
JustInTimeDetail &onOneOfAsyncBenchFinsihed(JustInTimeOnFinished fn) { | |
m_asyncBenchFinsihed = fn; | |
return *this; | |
} | |
JustInTimeDetail &onOneOfSyncBenchFinsihed(JustInTimeOnFinished fn) { | |
m_syncBenchFinsihed = fn; | |
return *this; | |
} | |
private: | |
static void startBench(JustInTimeBenchConfig &config) { | |
auto ignore_timing = [&config](JustInTimerIndependentFn timerIndependent) { | |
// main implementation: calculate the running time used to call the inner function timeIndependent | |
// and add the function call time difference(delta) to the "time ignored" variable | |
auto d1 = JustInTimeGetCurrentTime(); | |
timerIndependent(); | |
auto d2 = JustInTimeGetCurrentTime(); | |
config.time_ignored += d2 - d1; // add the time delta | |
}; | |
auto fnSelect = config.benchSite.value(); | |
if (std::holds_alternative<JustInTimeBenchmarkSiteWithIgnorer>(fnSelect)) { | |
// is with ignorer | |
auto benchFn = std::get<JustInTimeBenchmarkSiteWithIgnorer>(fnSelect); | |
config.started = JustInTimeGetCurrentTime(); | |
benchFn(ignore_timing); | |
config.finished = JustInTimeGetCurrentTime(); | |
} else { | |
// due to variant dichotomy it must be JustInTimeBenchmarkSite | |
auto benchFn = std::get<JustInTimeBenchmarkSite>(fnSelect); | |
config.started = JustInTimeGetCurrentTime(); | |
benchFn(); | |
config.finished = JustInTimeGetCurrentTime(); | |
} | |
} | |
void startAllAsyncBench(std::vector<std::shared_future<void>> &storage) { | |
for (auto &it : m_bench) { | |
if (it.async) { | |
auto fut = std::async([this, &it]() { // start async task | |
startBench(it); | |
if (m_asyncBenchFinsihed) { | |
m_asyncBenchFinsihed(it); | |
} | |
}).share(); | |
storage.push_back(fut); | |
} | |
} | |
} | |
void startAllSyncBench() { | |
for (auto &it : m_bench) { | |
if (!it.async) { | |
startBench(it); | |
if (m_syncBenchFinsihed) { | |
m_syncBenchFinsihed(it); | |
} | |
} | |
} | |
} | |
public: | |
JustInTimeDetail &startBench() { | |
if (m_bench.size() == 0) { // no entry is defined | |
return *this; | |
} | |
if (!configIsMarshalled()) { | |
throw "Benches were not properly configurated"; | |
} | |
/* | |
* main implmentation: start all asynchronous benchmarks first | |
* then start all synchronous benchmarks | |
*/ | |
std::vector<std::shared_future<void>> fut; | |
startAllAsyncBench(fut); | |
startAllSyncBench(); | |
for (auto &it : fut) { | |
it.wait(); | |
} | |
return *this; | |
} | |
JustInTimeBenchmarkResult getResults() { | |
return m_bench; | |
} | |
}; | |
public: | |
static JustInTimeDetail factory() { | |
return JustInTimeDetail(); | |
} | |
}; | |
static auto make_just_in_time() { | |
return JustInTime::factory(); | |
} | |
template<class T> | |
constexpr auto JustInTimeBenchConfig::getTimeElapsed() { // return the true time consumed by the benchmark function | |
auto total_function_timeframe = finished - started; | |
return std::chrono::duration_cast<T>(total_function_timeframe - time_ignored).count(); // implicitly ensuring type T to chrono::duration<T> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment