From 88eabf787676e3952ca3b993559a2d0a3842b11a Mon Sep 17 00:00:00 2001 From: shameel Date: Sat, 25 Feb 2023 20:15:29 +0530 Subject: [PATCH 1/9] Added profiling to console functionality over the existing implementation Added polymorphism for printing profiling results to the console. And can be easily invoked with `START_CONSOLE_SESSION` instead of `START_SESSION`, no other configurations are required. The existing `Instrumentor` is updated to `FilestreamInstrumentor`. --- benchmark.h | 101 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 23 deletions(-) diff --git a/benchmark.h b/benchmark.h index c33b55b..79c99b9 100644 --- a/benchmark.h +++ b/benchmark.h @@ -7,13 +7,12 @@ #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 +20,41 @@ struct InstrumentationSession { std::string Name; }; -class Instrumentor { +class IInstrumentor +{ + 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(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"; + 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; + SetCurrentSessionName(name); } - void EndSession() { + virtual void EndSession() { WriteFooter(); m_OutputStream.close(); delete m_CurrentSession; @@ -44,7 +62,7 @@ class Instrumentor { m_ProfileCount = 0; } - void WriteProfile(const ProfileResult& result) { + virtual void WriteProfile(const ProfileResult& result) { if (m_ProfileCount++ > 0) m_OutputStream << ","; @@ -73,13 +91,48 @@ 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); + } - static Instrumentor& Get() { - static Instrumentor* instance = new Instrumentor(); - return *instance; + virtual void EndSession() { + delete m_CurrentSession; + m_CurrentSession = nullptr; + } + + 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: " << (result.End - result.Start) << " microseconds, "; + std::cout << "ts: " << result.Start; + std::cout << std::endl; } }; +std::once_flag IInstrumentor::_creation_once_flag = std::once_flag(); + +IInstrumentor& IInstrumentor::Get(bool output_to_console) { + static IInstrumentor* instance = nullptr; + 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; +} + class InstrumentationTimer { const char* m_Name; std::chrono::time_point m_StartTimepoint; @@ -101,20 +154,22 @@ 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_SESSION(name) IInstrumentor::Get().BeginSession(name) +#define START_CONSOLE_SESSION(name) IInstrumentor::Get(true).BeginSession(name) +#define END_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 PROFILE_FUNCTION() +#define PROFILE_FUNCTION_DETAILED() +#endif + From ed51b46e8a19f6f8ef7459557ad2c416082dfda1 Mon Sep 17 00:00:00 2001 From: shameel Date: Sat, 25 Feb 2023 20:23:10 +0530 Subject: [PATCH 2/9] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f6a9626..f60f261 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,11 @@ Visual Benchmarking using C++ and Chrome Tracing for single threaded projects, m ``` START_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 ``` From e84ffb4270c0f283afbd9da8af9e1080a579f07d Mon Sep 17 00:00:00 2001 From: shameel Date: Mon, 27 Feb 2023 13:17:48 +0530 Subject: [PATCH 3/9] Made the profiling output to file thread safe --- benchmark.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/benchmark.h b/benchmark.h index 79c99b9..39880d4 100644 --- a/benchmark.h +++ b/benchmark.h @@ -3,7 +3,9 @@ #define ON 1 #define OFF 0 +#ifndef BENCHMARKING #define BENCHMARKING ON +#endif // BENCHMARKING #include #include @@ -45,13 +47,14 @@ class FilestreamInstrumentor : public IInstrumentor { std::ofstream m_OutputStream; int m_ProfileCount; const std::string filepath = "results.json"; + std::mutex write_mx; public: virtual void BeginSession(const std::string& name) { m_OutputStream.open(filepath); WriteHeader(); - SetCurrentSessionName(name); + __super::SetCurrentSessionName(name); } virtual void EndSession() { @@ -63,6 +66,7 @@ class FilestreamInstrumentor : public IInstrumentor { } virtual void WriteProfile(const ProfileResult& result) { + std::unique_lock lk(write_mx); if (m_ProfileCount++ > 0) m_OutputStream << ","; @@ -171,5 +175,7 @@ class InstrumentationTimer { #else #define PROFILE_FUNCTION() #define PROFILE_FUNCTION_DETAILED() +#define START_CONSOLE_SESSION(name) +#define END_SESSION() #endif From c3b707a18a66ea59485d3ae57b4dc4b5233dd47f Mon Sep 17 00:00:00 2001 From: shameel Date: Wed, 29 Mar 2023 23:38:04 +0530 Subject: [PATCH 4/9] Cant continue being header only Cant continue being a header only as there are static components that need to be initialized. If those are initialized in the header, they will get redefined errors if the header is used in multiple projects. --- Benchmark.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Benchmark.cpp 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; +} From f272ac19d1ab8461120c46f3a824aef048d2920d Mon Sep 17 00:00:00 2001 From: shameel Date: Wed, 29 Mar 2023 23:38:25 +0530 Subject: [PATCH 5/9] Rename Benchmark.cpp to benchmark.cpp --- Benchmark.cpp => benchmark.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Benchmark.cpp => benchmark.cpp (100%) diff --git a/Benchmark.cpp b/benchmark.cpp similarity index 100% rename from Benchmark.cpp rename to benchmark.cpp From 258d77924044b43c74357bbd87ed6eace78c6a22 Mon Sep 17 00:00:00 2001 From: shameel Date: Wed, 29 Mar 2023 23:46:10 +0530 Subject: [PATCH 6/9] Separated creation of Instrumentor from get() Fixed the issue of profiling happening before the START_BENCHMARK_SESSION. Throws an exception if the profiling is executed before start_benchmark_session is called. --- benchmark.h | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/benchmark.h b/benchmark.h index 39880d4..35e81ff 100644 --- a/benchmark.h +++ b/benchmark.h @@ -1,5 +1,12 @@ #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 @@ -10,6 +17,9 @@ #include #include #include +#include +#include +#include struct ProfileResult { std::string Name; @@ -24,6 +34,8 @@ struct InstrumentationSession { class IInstrumentor { +private: + static IInstrumentor* instance; static std::once_flag _creation_once_flag; public: IInstrumentor() : m_CurrentSession(nullptr) {} @@ -34,7 +46,8 @@ class IInstrumentor virtual void BeginSession(const std::string& name) = 0; virtual void WriteProfile(const ProfileResult& result) = 0; - static IInstrumentor& Get(bool output_to_console = false); + static IInstrumentor& Get(); + static IInstrumentor& Create(bool output_to_console = false); void SetCurrentSessionName(const std::string& name) { @@ -66,7 +79,7 @@ class FilestreamInstrumentor : public IInstrumentor { } virtual void WriteProfile(const ProfileResult& result) { - std::unique_lock lk(write_mx); + std::unique_lock lk(write_mx); if (m_ProfileCount++ > 0) m_OutputStream << ","; @@ -114,29 +127,14 @@ class ConsoleInstrumentator : public IInstrumentor { virtual void WriteProfile(const ProfileResult& result) { std::string name = result.Name; std::replace(name.begin(), name.end(), '"', '\''); - auto lk = std::unique_lock(cout_mx); + auto lk = std::unique_lock (cout_mx); std::cout << "name: " << name << ", "; - std::cout << "dur: " << (result.End - result.Start) << " microseconds, "; + std::cout << "dur: " << std::chrono::microseconds(result.End - result.Start).count() << "us, "; std::cout << "ts: " << result.Start; std::cout << std::endl; } }; -std::once_flag IInstrumentor::_creation_once_flag = std::once_flag(); - -IInstrumentor& IInstrumentor::Get(bool output_to_console) { - static IInstrumentor* instance = nullptr; - 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; -} - class InstrumentationTimer { const char* m_Name; std::chrono::time_point m_StartTimepoint; @@ -167,12 +165,14 @@ class InstrumentationTimer { #if BENCHMARKING #define PROFILE_SCOPE(name) InstrumentationTimer timer(name) -#define START_SESSION(name) IInstrumentor::Get().BeginSession(name) -#define START_CONSOLE_SESSION(name) IInstrumentor::Get(true).BeginSession(name) -#define END_SESSION() IInstrumentor::Get().EndSession() +#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 START_SESSION(name) +#define PROFILE_SCOPE(name) #define PROFILE_FUNCTION() #define PROFILE_FUNCTION_DETAILED() #define START_CONSOLE_SESSION(name) From 582e42fb7e1581893db5f63d48baad8ea02d7231 Mon Sep 17 00:00:00 2001 From: shameel Date: Wed, 29 Mar 2023 23:46:52 +0530 Subject: [PATCH 7/9] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f60f261..bd61b62 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 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 ``` @@ -14,7 +14,7 @@ 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 From f13a3c623dcc3a5162a8e8921116d5b277ce3b0a Mon Sep 17 00:00:00 2001 From: shameel Date: Wed, 29 Mar 2023 23:48:49 +0530 Subject: [PATCH 8/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd61b62..52cfc1f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ 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 From 335d44c17ea42351264747fb9f94172c3772f7d1 Mon Sep 17 00:00:00 2001 From: shameel Date: Mon, 1 Jul 2024 14:54:31 +0530 Subject: [PATCH 9/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52cfc1f..b70ad62 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ int main() { std::cout << fibonacci(number) << std::endl; //this code will be benchmarked - END_SESSION(); //ending the session + END_BENCHMARK_SESSION(); //ending the session } ```