SIRIUS 7.5.0
Electronic structure library and applications
rt_graph.hpp
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
40namespace rt_graph {
41
42using ClockType = std::chrono::high_resolution_clock;
43
44// Selection of available statistics
45enum class Stat {
46 Count, // Number of measurements
47 Total, // Total accumulated time
48 Self, // Total accumulated time minus total time of sub-timings
49 Mean, // Mean time
50 Median, // Median time
51 QuartileHigh, // Third quartile time
52 QuartileLow, // First quartile time
53 Min, // Mininum time
54 Max, // Maximum time
55 Percentage, // Percentage of accumulated time with respect to the top-level node in graph
56 ParentPercentage, // Percentage of accumulated time with respect to the parent node in graph
57 SelfPercentage // Percentage of accumulated time not spend in sub-timings
58};
59
60// internal helper functionality
61namespace internal {
62
63enum class TimeStampType { Start, Stop, Empty };
64
65struct TimeStamp {
66 TimeStamp() : type(TimeStampType::Empty) {}
67
68 // Identifier pointer must point to compile time string literal
69 TimeStamp(const char* identifier, const TimeStampType& stampType)
70 : time(ClockType::now()), identifierPtr(identifier), type(stampType) {}
71
72 ClockType::time_point time;
73 const char* identifierPtr;
74 TimeStampType type;
75};
76
77struct TimingNode {
78 std::string identifier;
79 std::vector<double> timings;
80 std::vector<double> startTimes;
81 std::list<TimingNode> subNodes;
82 double totalTime = 0.0;
83
84 inline void add_time(double startTime, double t) {
85 startTimes.push_back(startTime);
86 timings.push_back(t);
87 totalTime += t;
88 }
89};
90} // namespace internal
91
92// Processed timings results.
94public:
95 TimingResult(std::list<internal::TimingNode> rootNodes, std::string warnings)
96 : rootNodes_(std::move(rootNodes)), warnings_(std::move(warnings)) {}
97
98 // Get json representation of the full graph with all timings. Unit of time is seconds.
99 auto json() const -> std::string;
100
101 // Get all timings for given identifier
102 auto get_timings(const std::string& identifier) const -> std::vector<double>;
103
104 // Print graph statistic to string.
105 auto print(std::vector<Stat> statistic = {Stat::Count, Stat::Total, Stat::Percentage,
106 Stat::ParentPercentage, Stat::Median, Stat::Min,
107 Stat::Max}) const -> std::string;
108
109 // Flatten graph up to given level (level 0 equals root nodes), where Timings with the same string
110 // identifier are added together.
111 auto flatten(std::size_t level) -> TimingResult&;
112
113 // Sort nodes by total time.
114 auto sort_nodes() -> TimingResult&;
115
116private:
117 std::list<internal::TimingNode> rootNodes_;
118 std::string warnings_;
119};
120
121class ScopedTiming;
122
123// Timer class, which allows to start / stop measurements with a given identifier.
124class Timer {
125public:
126 // reserve space for 1000'000 measurements
127 Timer() { timeStamps_.reserve(2 * 1000 * 1000); }
128
129 // reserve space for given number of measurements
130 explicit Timer(std::size_t reserveCount) { timeStamps_.reserve(2 * reserveCount); }
131
132 // start with string literal identifier
133 template <std::size_t N>
134 inline auto start(const char (&identifierPtr)[N]) -> void {
135 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
136 timeStamps_.emplace_back(identifierPtr, internal::TimeStampType::Start);
137 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
138 }
139
140 // start with string identifier (storing string object comes with some additional overhead)
141 inline auto start(std::string identifier) -> void {
142 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
143 identifierStrings_.emplace_back(std::move(identifier));
144 timeStamps_.emplace_back(identifierStrings_.back().c_str(), internal::TimeStampType::Start);
145 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
146 }
147
148 // stop with string literal identifier
149 template <std::size_t N>
150 inline auto stop(const char (&identifierPtr)[N]) -> void {
151 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
152 timeStamps_.emplace_back(identifierPtr, internal::TimeStampType::Stop);
153 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
154 }
155
156 // stop with string identifier (storing string object comes with some additional overhead)
157 inline auto stop(std::string identifier) -> void {
158 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
159 identifierStrings_.emplace_back(std::move(identifier));
160 timeStamps_.emplace_back(identifierStrings_.back().c_str(), internal::TimeStampType::Stop);
161 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
162 }
163
164 // clear timer and reserve space for given number of new measurements.
165 inline auto clear(std::size_t reserveCount) -> void {
166 timeStamps_.clear();
167 identifierStrings_.clear();
168 this->reserve(reserveCount);
169 }
170
171 // reserve space for given number of measurements. Can prevent allocations at start / stop calls.
172 inline auto reserve(std::size_t reserveCount) -> void { timeStamps_.reserve(reserveCount); }
173
174 // process timings into result type
175 auto process() const -> TimingResult;
176
177private:
178 inline auto stop_with_ptr(const char* identifierPtr) -> void {
179 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
180 timeStamps_.emplace_back(identifierPtr, internal::TimeStampType::Stop);
181 atomic_signal_fence(std::memory_order_seq_cst); // only prevents compiler reordering
182 }
183
184 friend ScopedTiming;
185
186 std::vector<internal::TimeStamp> timeStamps_;
187 std::deque<std::string>
188 identifierStrings_; // pointer to elements always remain valid after push back
189};
190
191// Helper class, which calls start() upon creation and stop() on timer when leaving scope with given
192// identifier.
194public:
195 // timer reference must be valid for the entire lifetime
196 template <std::size_t N>
197 ScopedTiming(const char (&identifierPtr)[N], Timer& timer)
198 : identifierPtr_(identifierPtr), timer_(timer) {
199 timer_.start(identifierPtr);
200 }
201
202 ScopedTiming(std::string identifier, Timer& timer)
203 : identifierPtr_(nullptr), identifier_(std::move(identifier)), timer_(timer) {
204 timer_.start(identifier_);
205 }
206
207 ScopedTiming(const ScopedTiming&) = delete;
208 ScopedTiming(ScopedTiming&&) = delete;
209 auto operator=(const ScopedTiming&) -> ScopedTiming& = delete;
210 auto operator=(ScopedTiming &&) -> ScopedTiming& = delete;
211
212 ~ScopedTiming() {
213 if (identifierPtr_) {
214 timer_.stop_with_ptr(identifierPtr_);
215 } else {
216 timer_.stop(std::move(identifier_));
217 }
218 }
219
220private:
221 const char* identifierPtr_;
222 std::string identifier_;
223 Timer& timer_;
224};
225
226} // namespace rt_graph
227
228#endif
namespace for Niels Lohmann