-
Star
(235)
You must be signed in to star a gist -
Fork
(60)
You must be signed in to fork a gist
-
-
Save TheCherno/31f135eea6ee729ab5f26a6908eb3a5e to your computer and use it in GitHub Desktop.
| // | |
| // Basic instrumentation profiler by Cherno | |
| // Usage: include this header file somewhere in your code (eg. precompiled header), and then use like: | |
| // | |
| // Instrumentor::Get().BeginSession("Session Name"); // Begin session | |
| // { | |
| // InstrumentationTimer timer("Profiled Scope Name"); // Place code like this in scopes you'd like to include in profiling | |
| // // Code | |
| // } | |
| // Instrumentor::Get().EndSession(); // End Session | |
| // | |
| // You will probably want to macro-fy this, to switch on/off easily and use things like __FUNCSIG__ for the profile name. | |
| // | |
| #pragma once | |
| #include <string> | |
| #include <chrono> | |
| #include <algorithm> | |
| #include <fstream> | |
| #include <thread> | |
| struct ProfileResult | |
| { | |
| std::string Name; | |
| long long Start, End; | |
| uint32_t ThreadID; | |
| }; | |
| struct InstrumentationSession | |
| { | |
| std::string Name; | |
| }; | |
| class Instrumentor | |
| { | |
| private: | |
| InstrumentationSession* m_CurrentSession; | |
| std::ofstream m_OutputStream; | |
| int m_ProfileCount; | |
| public: | |
| Instrumentor() | |
| : m_CurrentSession(nullptr), m_ProfileCount(0) | |
| { | |
| } | |
| void BeginSession(const std::string& name, const std::string& filepath = "results.json") | |
| { | |
| m_OutputStream.open(filepath); | |
| WriteHeader(); | |
| m_CurrentSession = new InstrumentationSession{ name }; | |
| } | |
| void EndSession() | |
| { | |
| WriteFooter(); | |
| m_OutputStream.close(); | |
| delete m_CurrentSession; | |
| m_CurrentSession = nullptr; | |
| m_ProfileCount = 0; | |
| } | |
| void WriteProfile(const ProfileResult& result) | |
| { | |
| if (m_ProfileCount++ > 0) | |
| m_OutputStream << ","; | |
| std::string name = result.Name; | |
| std::replace(name.begin(), name.end(), '"', '\''); | |
| m_OutputStream << "{"; | |
| m_OutputStream << "\"cat\":\"function\","; | |
| m_OutputStream << "\"dur\":" << (result.End - result.Start) << ','; | |
| m_OutputStream << "\"name\":\"" << name << "\","; | |
| m_OutputStream << "\"ph\":\"X\","; | |
| m_OutputStream << "\"pid\":0,"; | |
| m_OutputStream << "\"tid\":" << result.ThreadID << ","; | |
| m_OutputStream << "\"ts\":" << result.Start; | |
| m_OutputStream << "}"; | |
| m_OutputStream.flush(); | |
| } | |
| void WriteHeader() | |
| { | |
| m_OutputStream << "{\"otherData\": {},\"traceEvents\":["; | |
| m_OutputStream.flush(); | |
| } | |
| void WriteFooter() | |
| { | |
| m_OutputStream << "]}"; | |
| m_OutputStream.flush(); | |
| } | |
| static Instrumentor& Get() | |
| { | |
| static Instrumentor instance; | |
| return instance; | |
| } | |
| }; | |
| class InstrumentationTimer | |
| { | |
| public: | |
| InstrumentationTimer(const char* name) | |
| : m_Name(name), m_Stopped(false) | |
| { | |
| m_StartTimepoint = std::chrono::high_resolution_clock::now(); | |
| } | |
| ~InstrumentationTimer() | |
| { | |
| if (!m_Stopped) | |
| Stop(); | |
| } | |
| void Stop() | |
| { | |
| auto endTimepoint = std::chrono::high_resolution_clock::now(); | |
| long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count(); | |
| long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count(); | |
| uint32_t threadID = std::hash<std::thread::id>{}(std::this_thread::get_id()); | |
| Instrumentor::Get().WriteProfile({ m_Name, start, end, threadID }); | |
| m_Stopped = true; | |
| } | |
| private: | |
| const char* m_Name; | |
| std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimepoint; | |
| bool m_Stopped; | |
| }; |
Is dur a valid field? I looked at the format docs (linked below) and cannot find it.
https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
@AbduEhab Maybe
std::is_constant_evaluated()in C++20 can help
That was exactly what I was looking for, thanks! Should be trivial to change the macros to accommodate it.
Is
dura valid field? I looked at the format docs (linked below) and cannot find it. https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
It is a valid field for the "Complete Events".
https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit#heading=h.lpfof2aylapb
This is not bad, but could be better. I recently put together a small repo which does a similar thing. https://github.com/sirgru/SimpleCppProfiler
In this code the data is put on the output_stream when the timer goes out of scope. Won't this skew the results if there are Multiple Timers running, that is, a function being timed calls another Function being timed , In this the Time to write is also considered
I needed to add one mutex in WriteProfile function to stop different threads writing to .json in the same time.
I added this line at the begging of WriteProfile function:
std::lock_guard<std::mutex> lock(Mutex);
@AbduEhab Maybe
std::is_constant_evaluated()in C++20 can help