diff --git a/src/gui/profilergraph.h b/src/gui/profilergraph.h index 6354ac9ef..1563d3c48 100644 --- a/src/gui/profilergraph.h +++ b/src/gui/profilergraph.h @@ -26,15 +26,28 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "profiler.h" -/* Profiler display */ + +/** \brief Draws graph every frame using data given by Profiler. + + It displays only entries that are recorded for profiler graph (via Profiler::graphAdd, + ScopeProfilerType::SPT_GRAPH_ADD and other) and Profiler itself doesn't show them. + + Each profiling entry described with upper bound value, entry name and lower bound + value on its right. Graph is displayed as \e line if it is relative (lower value + is not 0) and \e filled if it is absolute. + + \ingroup Profiling +*/ class ProfilerGraph { private: + /// One-frame slice of graph values. struct Piece { Piece(Profiler::GraphValues v) : values(std::move(v)) {} Profiler::GraphValues values; }; + /// Data for drawing one graph. Updates every frame. struct Meta { float min; @@ -54,6 +67,8 @@ class ProfilerGraph ProfilerGraph() = default; + /// Adds graph values to the end of graph (rendered at right side) and + /// removes the oldest ones (beyond the `m_log_max_size`). void put(const Profiler::GraphValues &values); void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver, diff --git a/src/profiler.h b/src/profiler.h index e7135d9c2..9ce055a1a 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -29,22 +29,63 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/timetaker.h" #include "util/numeric.h" // paging() -// Global profiler -class Profiler; -extern Profiler *g_profiler; -/* - Time profiler +/** \defgroup Profiling Profiling tools + \brief Embedded tools to profile CPU time or anything else that is recorded + by certain functions. + + An example with global profiler (#g_profiler), that is available in any file + with profiler.h included: + \code{.c++} + void functionToProfie(int count) { + // Record (add) time till function (scope) end + ScopeProfiler scope_profiler(g_profiler, "functionToProfie()"); // SPT_ADD by default + // Record (add) `count` + g_profiler->add("functionToProfie() count", count) + } + \endcode + In this example "add" method is used, so if function is called several times during + g_profiler cycle, sum of all recorded values will be shown at cycle end. See + #g_profiler for details about cycle and info showing. + + TimeTaker can be used instead of ScopeProfiler. + \{ */ + +/** \brief Collects values (time intervals, counts and other) in named entries. + + Can be used to get sum, average or maximum of recorded values for each entry. Gives + result in text form via print(). + + Can collect values for #ProfilerGraph and give them via graphPop(). + + For usage, see \ref Profiling. +*/ class Profiler { public: Profiler(); + /** \brief Records the \p value in entry with given \p name to get sum of recorded values. + \param[in] name: entry name; can be same for several function calls + \param[in] value: value to record + */ void add(const std::string &name, float value); + + /** \brief Records the \p value in entry with given \p name to get average of recorded values. + \param[in] name: entry name; can be same for several function calls + \param[in] value: value to record + */ void avg(const std::string &name, float value); + + /** \brief Records the \p value in entry with given \p name to get maximum of recorded values. + \param[in] name: entry name; can be same for several function calls + \param[in] value: value to record + */ void max(const std::string &name, float value); + + /// Clears all profiling entries (#m_data). Does not affect #m_graphvalues. void clear(); float getValue(const std::string &name) const; @@ -53,16 +94,46 @@ class Profiler typedef std::map GraphValues; - // Returns the line count + /** \brief Prints collected data formatted as table. + + Prints each entry as one line which contains name, avgCount and value. + Meaning of printed numbers depends on used recording method (add/avg/max). + avgCount makes sense only for "avg" method and shows number of records; + always shows 1 for other methods. + + Breaks all entries into \p pagecount pages and print only given \p page. + + \param[out] o: stream to print into + \param[in] page: number of page to print + \param[in] pagecount: count of pages to break data into + \returns printed line count + */ int print(std::ostream &o, u32 page = 1, u32 pagecount = 1); + + /** \brief Breaks #m_data (entries) into \p pagecount pages (GraphValues) and + writes values on given \p page into \p o. + \param[out] o: variable to write page into + \param[in] page: page to get + \param[in] pagecount: number of page to break data into + */ void getPage(GraphValues &o, u32 page, u32 pagecount); - + /** \brief Records the \p value in graph entry with given \p id (name) to get + exactly this value. + \param[in] id: entry name + \param[in] value: value to record + */ void graphSet(const std::string &id, float value) { MutexAutoLock lock(m_mutex); m_graphvalues[id] = value; } + + /** \brief Records the \p value in graph entry with given \p id (name) to get + sum of recorded values. + \param[in] id: entry name + \param[in] value: value to record + */ void graphAdd(const std::string &id, float value) { MutexAutoLock lock(m_mutex); @@ -72,6 +143,9 @@ class Profiler else it->second += value; } + + /// \brief Gives m_graphvalues into \p result. + /// \param[out] result: Variable to record values into void graphPop(GraphValues &result) { MutexAutoLock lock(m_mutex); @@ -79,6 +153,8 @@ class Profiler std::swap(result, m_graphvalues); } + /// \brief Removes entry with given \p name. + /// \param[in] name: Name of entry to remove void remove(const std::string& name) { MutexAutoLock lock(m_mutex); @@ -87,7 +163,10 @@ class Profiler private: struct DataPair { + /// Recorded value. Recording method depends on used functions (add/avg/max) + /// of profiler. float value = 0; + /// Count of records if "avg" method is used. int avgcount = 0; inline void reset() { @@ -102,33 +181,69 @@ class Profiler }; std::mutex m_mutex; + /// All the profiler entries, stored until `clear()` call. std::map m_data; + /// Values for profiler graph collected until next frame draw. Value history + /// is stored in `ProfilerGraph`. std::map m_graphvalues; u64 m_start_time; }; + +/** \brief Global profiler. User can view its data via in-game profiler menu, + graph or "/profiler" command. + + Profiling cycles are managed in Game class: + + - cycle for profiler menu (GameUI::updateProfiler()) is managed in + Game::updateProfilers() and lasts for \c profiler_print_interval seconds (user + setting) or for 3 seconds if setting is set to 0; + + - cycle for ProfilerGraph is managed in Game::updateProfilerGraphs() and lasts + for one frame; +*/ +extern Profiler* g_profiler; + + enum ScopeProfilerType : u8 { + /// Record time with Profiler::add() (to get sum of recorded values) at scope end. SPT_ADD = 1, + /// Record time with Profiler::avg() (to get average of recorded values) at scope end. SPT_AVG, + /// Record time with Profiler::graphAdd() (to get sum of recorded values on + /// ProfilerGraph) at scope end. SPT_GRAPH_ADD, + /// Record time with Profiler::max() (to get maximum of recorded values) at scope end. SPT_MAX }; -// Note: this class should be kept lightweight. +/** \brief A class for time measurements. Each created object records time from its + construction till scope end. + + Note: this class should be kept lightweight. +*/ class ScopeProfiler { public: + /** \brief Begins measurement until scope end.Result will be recorded to + \p name + profiling entry in \p profiler (profiling entries are created/deleted automatically). + */ ScopeProfiler(Profiler *profiler, const std::string &name, ScopeProfilerType type = SPT_ADD, TimePrecision precision = PRECISION_MILLI); + + /// Ends measurement and record result in profiler. ~ScopeProfiler(); private: - Profiler *m_profiler = nullptr; - std::string m_name; - u64 m_time1; + Profiler *m_profiler = nullptr; /// Profiler to record in + std::string m_name; /// Name of profiler entry to record in + u64 m_time1; /// Record start time ScopeProfilerType m_type; TimePrecision m_precision; }; + +/** \} */