diff --git a/README.md b/README.md index f6a9626..b70ad62 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,15 @@ Visual Benchmarking using C++ and Chrome Tracing for single threaded projects, m ##### To start the session for benchmarking, call this function ``` -START_SESSION("Session Name"); +START_BENCHMARK_SESSION("Session Name"); ``` +This profiles into a file that can be opened in Chrome tracing to view the benchmarking results +``` +START_CONSOLE_SESSION("Session Name"); +``` +This writes the benchmarked readings into the console. -##### To profile any function in your benchmark while your session is active, add this function in the beginning of the code of your function +##### To profile any function in your benchmark while your session is active, add this function in the beginning of the code of your function ``` PROFILE_FUNCTION(); or @@ -38,11 +43,11 @@ int fibonacci(int number) { int main() { int number; std::cin >> number; - START_SESSION("Fibonacci"); //starting the session from where benchmarking starts + START_BENCHMARK_SESSION("Fibonacci"); //starting the session from where benchmarking starts std::cout << fibonacci(number) << std::endl; //this code will be benchmarked - END_SESSION(); //ending the session + END_BENCHMARK_SESSION(); //ending the session } ``` diff --git a/benchmark.cpp b/benchmark.cpp new file mode 100644 index 0000000..e6ce697 --- /dev/null +++ b/benchmark.cpp @@ -0,0 +1,23 @@ +#include "Benchmark.h" + + +std::once_flag IInstrumentor::_creation_once_flag; +IInstrumentor* IInstrumentor::instance = nullptr; + +IInstrumentor& IInstrumentor::Get() { + if(instance) return *instance; + + throw std::exception("Instrumentor instance not created"); +} + +IInstrumentor& IInstrumentor::Create(bool output_to_console) { + std::call_once(_creation_once_flag, + [output_to_console]() { + if (output_to_console) + instance = new ConsoleInstrumentator(); + else + instance = new FilestreamInstrumentor(); + } + ); //thread safe initialization + return *instance; +} diff --git a/benchmark.h b/benchmark.h index c33b55b..35e81ff 100644 --- a/benchmark.h +++ b/benchmark.h @@ -1,19 +1,30 @@ #pragma once +/* +* This header only library was taken from +* https://github.com/DefinitelyNotAnmol/VisualBenchmarking/blob/master/benchmark.h +* And customized to add an option of profiling output to console using polymorphism +* Customized version : https://github.com/mhdshameel/VisualBenchmarking +* */ + #define ON 1 #define OFF 0 +#ifndef BENCHMARKING #define BENCHMARKING ON +#endif // BENCHMARKING #include #include -#include #include +#include +#include +#include struct ProfileResult { std::string Name; long long Start, End; - ProfileResult(const char* name, long long start, long long end) + ProfileResult(const char* name, long long start, long long end) : Name(name), Start(start), End(end) {} }; @@ -21,22 +32,45 @@ struct InstrumentationSession { std::string Name; }; -class Instrumentor { +class IInstrumentor +{ +private: + static IInstrumentor* instance; + static std::once_flag _creation_once_flag; +public: + IInstrumentor() : m_CurrentSession(nullptr) {} InstrumentationSession* m_CurrentSession; + + virtual ~IInstrumentor() {} + virtual void EndSession() = 0; + virtual void BeginSession(const std::string& name) = 0; + virtual void WriteProfile(const ProfileResult& result) = 0; + + static IInstrumentor& Get(); + static IInstrumentor& Create(bool output_to_console = false); + + void SetCurrentSessionName(const std::string& name) + { + m_CurrentSession = new InstrumentationSession; + m_CurrentSession->Name = name; + } +}; + +class FilestreamInstrumentor : public IInstrumentor { std::ofstream m_OutputStream; int m_ProfileCount; + const std::string filepath = "results.json"; + std::mutex write_mx; + public: - Instrumentor() - : m_CurrentSession(nullptr), m_ProfileCount(0) {} - void BeginSession(const std::string& name, const std::string& filepath = "results.json") { + virtual void BeginSession(const std::string& name) { m_OutputStream.open(filepath); WriteHeader(); - m_CurrentSession = new InstrumentationSession; - m_CurrentSession->Name = name; + __super::SetCurrentSessionName(name); } - void EndSession() { + virtual void EndSession() { WriteFooter(); m_OutputStream.close(); delete m_CurrentSession; @@ -44,7 +78,8 @@ class Instrumentor { m_ProfileCount = 0; } - void WriteProfile(const ProfileResult& result) { + virtual void WriteProfile(const ProfileResult& result) { + std::unique_lock lk(write_mx); if (m_ProfileCount++ > 0) m_OutputStream << ","; @@ -73,10 +108,30 @@ class Instrumentor { m_OutputStream << "]}"; m_OutputStream.flush(); } +}; + +class ConsoleInstrumentator : public IInstrumentor { + std::mutex cout_mx; +public: + ConsoleInstrumentator() {} + + virtual void BeginSession(const std::string& name) { + SetCurrentSessionName(name); + } + + virtual void EndSession() { + delete m_CurrentSession; + m_CurrentSession = nullptr; + } - static Instrumentor& Get() { - static Instrumentor* instance = new Instrumentor(); - return *instance; + virtual void WriteProfile(const ProfileResult& result) { + std::string name = result.Name; + std::replace(name.begin(), name.end(), '"', '\''); + auto lk = std::unique_lock (cout_mx); + std::cout << "name: " << name << ", "; + std::cout << "dur: " << std::chrono::microseconds(result.End - result.Start).count() << "us, "; + std::cout << "ts: " << result.Start; + std::cout << std::endl; } }; @@ -101,20 +156,26 @@ class InstrumentationTimer { long long start = std::chrono::time_point_cast(m_StartTimepoint).time_since_epoch().count(); long long end = std::chrono::time_point_cast(endTimepoint).time_since_epoch().count(); - - Instrumentor::Get().WriteProfile(ProfileResult(m_Name, start, end)); + + IInstrumentor::Get().WriteProfile(ProfileResult(m_Name, start, end)); m_Stopped = true; } }; #if BENCHMARKING - #define PROFILE_SCOPE(name) InstrumentationTimer timer(name) - #define START_SESSION(name) Instrumentor::Get().BeginSession(name) - #define END_SESSION() Instrumentor::Get().EndSession() - #define PROFILE_FUNCTION() PROFILE_SCOPE(__FUNCTION__) - #define PROFILE_FUNCTION_DETAILED() PROFILE_SCOPE(__PRETTY_FUNCTION__) +#define PROFILE_SCOPE(name) InstrumentationTimer timer(name) +#define START_BENCHMARK_SESSION(name) IInstrumentor::Create(false).BeginSession(name) +#define START_CONSOLE_SESSION(name) IInstrumentor::Create(true).BeginSession(name) +#define END_BENCHMARK_SESSION() IInstrumentor::Get().EndSession() +#define PROFILE_FUNCTION() PROFILE_SCOPE(__FUNCTION__) +#define PROFILE_FUNCTION_DETAILED() PROFILE_SCOPE(__PRETTY_FUNCTION__) #else - #define PROFILE_FUNCTION() - #define PROFILE_FUNCTION_DETAILED() -#endif \ No newline at end of file +#define START_SESSION(name) +#define PROFILE_SCOPE(name) +#define PROFILE_FUNCTION() +#define PROFILE_FUNCTION_DETAILED() +#define START_CONSOLE_SESSION(name) +#define END_SESSION() +#endif +