1 /* 2 * Copyright (c) 2019 Simon Frasch 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the names of its contributors 13 * may be used to endorse or promote products derived from this software 14 * without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #ifndef RT_GRAPH_HPP_GUARD 30 #define RT_GRAPH_HPP_GUARD 31 32 #include <atomic> 33 #include <chrono> 34 #include <cstddef> 35 #include <deque> 36 #include <list> 37 #include <string> 38 #include <vector> 39 40 namespace rt_graph { 41 42 using ClockType = std::chrono::high_resolution_clock; 43 44 // Selection of available statistics 45 enum class Stat { 46 Count, // Number of measurements 47 Total, // Total accumulated time 48 Mean, // Mean time 49 Median, // Median time 50 QuartileHigh, // Third quartile time 51 QuartileLow, // First quartile time 52 Min, // Mininum time 53 Max, // Maximum time 54 Percentage, // Percentage of accumulated time with respect to the top-level node in graph 55 ParentPercentage // Percentage of accumulated time with respect to the parent node in graph 56 }; 57 58 // internal helper functionality 59 namespace internal { 60 61 enum class TimeStampType { Start, Stop, Empty }; 62 63 struct TimeStamp { TimeStamprt_graph::internal::TimeStamp64 TimeStamp() : type(TimeStampType::Empty) {} 65 66 // Identifier pointer must point to compile time string literal TimeStamprt_graph::internal::TimeStamp67 TimeStamp(const char* identifier, const TimeStampType& stampType) 68 : time(ClockType::now()), identifierPtr(identifier), type(stampType) {} 69 70 ClockType::time_point time; 71 const char* identifierPtr; 72 TimeStampType type; 73 }; 74 75 struct TimingNode { 76 std::string identifier; 77 std::vector<double> timings; 78 std::list<TimingNode> subNodes; 79 }; 80 } // namespace internal 81 82 // Processed timings results. 83 class TimingResult { 84 public: TimingResult(std::list<internal::TimingNode> rootNodes,std::string warnings)85 TimingResult(std::list<internal::TimingNode> rootNodes, std::string warnings) 86 : rootNodes_(std::move(rootNodes)), warnings_(std::move(warnings)) {} 87 88 // Get json representation of the full graph with all timings. Unit of time is seconds. 89 auto json() const -> std::string; 90 91 // Get all timings for given identifier 92 auto get_timings(const std::string& identifier) const -> std::vector<double>; 93 94 // Print graph statistic to string. 95 auto print(std::vector<Stat> statistic = {Stat::Count, Stat::Total, Stat::Percentage, 96 Stat::ParentPercentage, Stat::Median, Stat::Min, 97 Stat::Max}) const -> std::string; 98 99 private: 100 std::list<internal::TimingNode> rootNodes_; 101 std::string warnings_; 102 }; 103 104 class ScopedTiming; 105 106 // Timer class, which allows to start / stop measurements with a given identifier. 107 class Timer { 108 public: 109 // reserve space for 1000'000 measurements Timer()110 Timer() { timeStamps_.reserve(2 * 1000 * 1000); } 111 112 // reserve space for given number of measurements Timer(std::size_t reserveCount)113 explicit Timer(std::size_t reserveCount) { timeStamps_.reserve(2 * reserveCount); } 114 115 // start with string literal identifier 116 template <std::size_t N> start(const char (& identifierPtr)[N])117 inline auto start(const char (&identifierPtr)[N]) -> void { 118 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 119 timeStamps_.emplace_back(identifierPtr, internal::TimeStampType::Start); 120 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 121 } 122 123 // start with string identifier (storing string object comes with some additional overhead) start(std::string identifier)124 inline auto start(std::string identifier) -> void { 125 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 126 identifierStrings_.emplace_back(std::move(identifier)); 127 timeStamps_.emplace_back(identifierStrings_.back().c_str(), internal::TimeStampType::Start); 128 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 129 } 130 131 // stop with string literal identifier 132 template <std::size_t N> stop(const char (& identifierPtr)[N])133 inline auto stop(const char (&identifierPtr)[N]) -> void { 134 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 135 timeStamps_.emplace_back(identifierPtr, internal::TimeStampType::Stop); 136 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 137 } 138 139 // stop with string identifier (storing string object comes with some additional overhead) stop(std::string identifier)140 inline auto stop(std::string identifier) -> void { 141 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 142 identifierStrings_.emplace_back(std::move(identifier)); 143 timeStamps_.emplace_back(identifierStrings_.back().c_str(), internal::TimeStampType::Stop); 144 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 145 } 146 147 // clear timer and reserve space for given number of new measurements. clear(std::size_t reserveCount)148 inline auto clear(std::size_t reserveCount) -> void { 149 timeStamps_.clear(); 150 identifierStrings_.clear(); 151 this->reserve(reserveCount); 152 } 153 154 // reserve space for given number of measurements. Can prevent allocations at start / stop calls. reserve(std::size_t reserveCount)155 inline auto reserve(std::size_t reserveCount) -> void { timeStamps_.reserve(reserveCount); } 156 157 // process timings into result type 158 auto process() const -> TimingResult; 159 160 private: stop_with_ptr(const char * identifierPtr)161 inline auto stop_with_ptr(const char* identifierPtr) -> void { 162 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 163 timeStamps_.emplace_back(identifierPtr, internal::TimeStampType::Stop); 164 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering 165 } 166 167 friend ScopedTiming; 168 169 std::vector<internal::TimeStamp> timeStamps_; 170 std::deque<std::string> 171 identifierStrings_; // pointer to elements always remain valid after push back 172 }; 173 174 // Helper class, which calls start() upon creation and stop() on timer when leaving scope with given 175 // identifier. 176 class ScopedTiming { 177 public: 178 // timer reference must be valid for the entire lifetime 179 template <std::size_t N> ScopedTiming(const char (& identifierPtr)[N],Timer & timer)180 ScopedTiming(const char (&identifierPtr)[N], Timer& timer) 181 : identifierPtr_(identifierPtr), timer_(timer) { 182 timer_.start(identifierPtr); 183 } 184 ScopedTiming(std::string identifier,Timer & timer)185 ScopedTiming(std::string identifier, Timer& timer) 186 : identifierPtr_(nullptr), identifier_(std::move(identifier)), timer_(timer) { 187 timer_.start(identifier_); 188 } 189 190 ScopedTiming(const ScopedTiming&) = delete; 191 ScopedTiming(ScopedTiming&&) = delete; 192 auto operator=(const ScopedTiming&) -> ScopedTiming& = delete; 193 auto operator=(ScopedTiming &&) -> ScopedTiming& = delete; 194 ~ScopedTiming()195 ~ScopedTiming() { 196 if (identifierPtr_) { 197 timer_.stop_with_ptr(identifierPtr_); 198 } else { 199 timer_.stop(std::move(identifier_)); 200 } 201 } 202 203 private: 204 const char* identifierPtr_; 205 std::string identifier_; 206 Timer& timer_; 207 }; 208 209 } // namespace rt_graph 210 211 #endif 212 213