/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* Profiler.h :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/10 13:35:45 by maldavid #+# #+# */ /* Updated: 2024/03/27 21:19:01 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef __MLX_PROFILER__ #define __MLX_PROFILER__ #include namespace mlx { using FloatingPointMilliseconds = std::chrono::duration; struct ProfileResult { std::string name; FloatingPointMilliseconds elapsed_time; std::thread::id thread_id; }; class Profiler : public Singleton { friend class Singleton; public: Profiler(const Profiler&) = delete; Profiler(Profiler&&) = delete; void AppendProfileData(ProfileResult&& result); private: Profiler() { BeginRuntimeSession(); } ~Profiler(); void BeginRuntimeSession(); void WriteProfile(const ProfileResult& result); void EndRuntimeSession(); inline void WriteHeader() { m_output_stream << "{\"profileData\":[{}"; m_output_stream.flush(); } inline void WriteFooter() { m_output_stream << "]}"; m_output_stream.flush(); } private: std::unordered_map> m_profile_data; std::ofstream m_output_stream; std::mutex m_mutex; bool m_runtime_session_began = false; }; class ProfilerTimer { public: ProfilerTimer(const char* name) : m_name(name) { m_start_timepoint = std::chrono::steady_clock::now(); } inline void Stop() { auto end_timepoint = std::chrono::steady_clock::now(); auto high_res_start = FloatingPointMilliseconds{ m_start_timepoint.time_since_epoch() }; auto elapsed_time = std::chrono::time_point_cast(end_timepoint).time_since_epoch() - std::chrono::time_point_cast(m_start_timepoint).time_since_epoch(); Profiler::get().appendProfileData({ m_name, elapsed_time, std::this_thread::get_id() }); m_stopped = true; } ~ProfilerTimer() { if(!m_stopped) stop(); } private: std::chrono::time_point m_start_timepoint; const char* m_name; bool m_stopped = false; }; namespace ProfilerUtils { template struct ChangeResult { char data[N]; }; template constexpr auto CleanupOutputString(const char(&expr)[N], const char(&remove)[K]) { ChangeResult result = {}; std::size_t src_index = 0; std::size_t dst_index = 0; while(src_index < N) { std::size_t match_index = 0; while(match_index < K - 1 && src_index + match_index < N - 1 && expr[src_index + match_index] == remove[match_index]) match_index++; if(match_index == K - 1) src_index += match_index; result.data[dst_index++] = expr[src_index] == '"' ? '\'' : expr[src_index]; src_index++; } return result; } } } #ifdef PROFILER #define MLX_PROFILE_SCOPE_LINE2(name, line) constexpr auto fixed_name_##line = ::mlx::ProfilerUtils::CleanupOutputString(name, "__cdecl ");\ ::mlx::ProfilerTimer timer##line(fixed_name_##line.data) #define MLX_PROFILE_SCOPE_LINE(name, line) MLX_PROFILE_SCOPE_LINE2(name, line) #define MLX_PROFILE_SCOPE(name) MLX_PROFILE_SCOPE_LINE(name, __LINE__) #define MLX_PROFILE_FUNCTION() MLX_PROFILE_SCOPE(MLX_FUNC_SIG) #else #define MLX_PROFILE_SCOPE(name) #define MLX_PROFILE_FUNCTION() #endif #endif