pure-cpp 1.0.0
A C++ physics simulation benchmark comparing performance with Python implementations
app_profiler.hpp
Go to the documentation of this file.
1/**
2 * \file app_profiler.hpp
3 * \brief Global application profiling instrumentation.
4 * \author Le Bars, Yoann
5 *
6 * This file provides profiling instrumentation for application-wide phases:
7 * - Qt initialization
8 * - 3D window creation
9 * - Frame rendering
10 * - Cleanup
11 */
12
13#ifndef APP_PROFILER_HPP
14#define APP_PROFILER_HPP
15
16#include <QDebug>
17#include <chrono>
18
19namespace AppUtils {
20 /**
21 * \brief Global application profiler for measuring non-physics phases.
22 *
23 * This class accumulates timing data for application-wide operations
24 * that are not part of the physics simulation itself.
25 */
27 public:
28 using Clock = std::chrono::high_resolution_clock;
29 using TimePoint = Clock::time_point;
30
31 /**
32 * \brief Starts timing Qt initialization.
33 */
34 static void startQtInit() { instance().qtInitStart_ = Clock::now(); }
35
36 /**
37 * \brief Stops timing Qt initialization.
38 */
39 static void stopQtInit() {
40 auto& inst = instance();
41 if (inst.qtInitStart_ != TimePoint{}) {
42 inst.qtInitTime_ += std::chrono::duration<double>(
43 Clock::now() - inst.qtInitStart_)
44 .count();
45 inst.qtInitStart_ = TimePoint{};
46 }
47 }
48
49 /**
50 * \brief Starts timing 3D window creation.
51 */
52 static void startWindowCreation() {
53 instance().windowCreationStart_ = Clock::now();
54 }
55
56 /**
57 * \brief Stops timing 3D window creation.
58 */
59 static void stopWindowCreation() {
60 auto& inst = instance();
61 if (inst.windowCreationStart_ != TimePoint{}) {
62 inst.windowCreationTime_ +=
63 std::chrono::duration<double>(Clock::now() -
64 inst.windowCreationStart_)
65 .count();
66 inst.windowCreationStart_ = TimePoint{};
67 }
68 }
69
70 /**
71 * \brief Starts timing a frame render.
72 */
73 static void startFrameRender() {
74 instance().frameRenderStart_ = Clock::now();
75 }
76
77 /**
78 * \brief Stops timing a frame render.
79 */
80 static void stopFrameRender() {
81 auto& inst = instance();
82 if (inst.frameRenderStart_ != TimePoint{}) {
83 inst.frameRenderTime_ +=
84 std::chrono::duration<double>(Clock::now() -
85 inst.frameRenderStart_)
86 .count();
87 inst.frameRenderCount_++;
88 inst.frameRenderStart_ = TimePoint{};
89 }
90 }
91
92 /**
93 * \brief Starts timing the Qt event loop.
94 */
95 static void startEventLoop() {
96 instance().eventLoopStart_ = Clock::now();
97 }
98
99 /**
100 * \brief Stops timing the Qt event loop.
101 */
102 static void stopEventLoop() {
103 auto& inst = instance();
104 if (inst.eventLoopStart_ != TimePoint{}) {
105 inst.eventLoopTime_ += std::chrono::duration<double>(
106 Clock::now() - inst.eventLoopStart_)
107 .count();
108 inst.eventLoopStart_ = TimePoint{};
109 }
110 }
111
112 /**
113 * \brief Starts timing cleanup.
114 */
115 static void startCleanup() { instance().cleanupStart_ = Clock::now(); }
116
117 /**
118 * \brief Stops timing cleanup.
119 */
120 static void stopCleanup() {
121 auto& inst = instance();
122 if (inst.cleanupStart_ != TimePoint{}) {
123 inst.cleanupTime_ += std::chrono::duration<double>(
124 Clock::now() - inst.cleanupStart_)
125 .count();
126 inst.cleanupStart_ = TimePoint{};
127 }
128 }
129
130 /**
131 * \brief Adds measured time for Display cleanup preparation.
132 *
133 * This phase includes stopping timers, disconnecting signals and
134 * stopping simulation scheduling.
135 */
136 static void addCleanupDisplayPrep(double seconds) {
137 instance().cleanupDisplayPrepTime_ += seconds;
138 }
139
140 /**
141 * \brief Adds measured time spent in Display cleanup processEvents.
142 */
143 static void addCleanupDisplayProcessEvents(double seconds) {
144 instance().cleanupDisplayProcessEventsTime_ += seconds;
145 }
146
147 /**
148 * \brief Adds measured time for stopping the physics thread.
149 */
150 static void addCleanupDisplayThreadStop(double seconds) {
151 instance().cleanupDisplayThreadStopTime_ += seconds;
152 }
153
154 /**
155 * \brief Adds measured time for physics worker cleanup.
156 */
157 static void addCleanupDisplayWorkerCleanup(double seconds) {
158 instance().cleanupDisplayWorkerCleanupTime_ += seconds;
159 }
160
161 /**
162 * \brief Adds measured time spent in bridge/display cleanup call.
163 */
164 static void addCleanupAppBridgeCleanup(double seconds) {
165 instance().cleanupAppBridgeCleanupTime_ += seconds;
166 }
167
168 /**
169 * \brief Adds measured time for manual destroy operations.
170 *
171 * This phase includes detach-from-window, display release and
172 * container deletion, but excludes processEvents.
173 */
174 static void addCleanupAppManualDestroy(double seconds) {
175 instance().cleanupAppManualDestroyTime_ += seconds;
176 }
177
178 /**
179 * \brief Adds measured time for detaching the display container.
180 */
181 static void addCleanupAppDetachContainer(double seconds) {
182 instance().cleanupAppDetachContainerTime_ += seconds;
183 }
184
185 /**
186 * \brief Adds measured time for explicit display release.
187 */
188 static void addCleanupAppReleaseDisplay(double seconds) {
189 instance().cleanupAppReleaseDisplayTime_ += seconds;
190 }
191
192 /**
193 * \brief Adds measured time for container delete action.
194 *
195 * In fast mode this is deleteLater() scheduling, in strict mode this is
196 * immediate deletion.
197 */
198 static void addCleanupAppContainerDeleteSchedule(double seconds) {
199 instance().cleanupAppContainerDeleteScheduleTime_ += seconds;
200 }
201
202 /**
203 * \brief Adds measured time spent in AppManager cleanup processEvents.
204 */
205 static void addCleanupAppProcessEvents(double seconds) {
206 instance().cleanupAppProcessEventsTime_ += seconds;
207 }
208
209 /**
210 * \brief Prints the profiling report.
211 */
212 static void printReport() {
213 const auto& inst = instance();
214 const double totalTime =
215 inst.qtInitTime_ + inst.windowCreationTime_ +
216 inst.eventLoopTime_ + inst.frameRenderTime_ + inst.cleanupTime_;
217
218 if (totalTime < 1e-9) {
219 qDebug().noquote()
220 << "\n--- Application Profiling Report (No data) ---";
221 return;
222 }
223
224 const double avgFrameMs =
225 inst.frameRenderCount_ > 0
226 ? inst.frameRenderTime_ / inst.frameRenderCount_ * 1000.0
227 : 0.0;
228 const QString msg =
229 QString(
230 "\n--- Application Profiling Report ---\nQt "
231 "Initialization: %1s (%2%%)\n3D Window Creation: "
232 "%3s (%4%%)\nQt Event Loop: %5s (%6%%)\nFrame "
233 "Rendering: %7s (%8%%) (%9 frames, avg: "
234 "%10ms/frame)\nCleanup: %11s "
235 "(%12%%)\n------------------------\nTotal Application "
236 "Time: %13s")
237 .arg(inst.qtInitTime_, 9, 'f', 4)
238 .arg(inst.qtInitTime_ / totalTime * 100.0, 5, 'f', 1)
239 .arg(inst.windowCreationTime_, 9, 'f', 4)
240 .arg(inst.windowCreationTime_ / totalTime * 100.0, 5, 'f',
241 1)
242 .arg(inst.eventLoopTime_, 9, 'f', 4)
243 .arg(inst.eventLoopTime_ / totalTime * 100.0, 5, 'f', 1)
244 .arg(inst.frameRenderTime_, 9, 'f', 4)
245 .arg(inst.frameRenderTime_ / totalTime * 100.0, 5, 'f', 1)
246 .arg(inst.frameRenderCount_)
247 .arg(avgFrameMs, 0, 'f', 4)
248 .arg(inst.cleanupTime_, 9, 'f', 4)
249 .arg(inst.cleanupTime_ / totalTime * 100.0, 5, 'f', 1)
250 .arg(totalTime, 9, 'f', 4);
251 qDebug().noquote() << msg;
252
253 if (inst.cleanupTime_ > 1e-9) {
254 const QString cleanupMsg =
255 QString(
256 "\n--- Cleanup Breakdown ---\nDisplay Prep: "
257 "%1s (%2%%)\nDisplay processEvents:%3s "
258 "(%4%%)\nDisplay Thread Stop:%5s (%6%%)\nDisplay "
259 "Worker Cleanup:%7s (%8%%)\nBridge "
260 "Cleanup*: %9s (%10%%)\nManual "
261 "Destroy: %11s (%12%%)\nApp processEvents: "
262 "%13s (%14%%)\n*Bridge Cleanup includes Display "
263 "sub-phases\n------------------------")
264 .arg(inst.cleanupDisplayPrepTime_, 9, 'f', 4)
265 .arg(inst.cleanupDisplayPrepTime_ / inst.cleanupTime_ *
266 100.0,
267 5, 'f', 1)
268 .arg(inst.cleanupDisplayProcessEventsTime_, 9, 'f', 4)
269 .arg(inst.cleanupDisplayProcessEventsTime_ /
270 inst.cleanupTime_ * 100.0,
271 5, 'f', 1)
272 .arg(inst.cleanupDisplayThreadStopTime_, 9, 'f', 4)
273 .arg(inst.cleanupDisplayThreadStopTime_ /
274 inst.cleanupTime_ * 100.0,
275 5, 'f', 1)
276 .arg(inst.cleanupDisplayWorkerCleanupTime_, 9, 'f', 4)
277 .arg(inst.cleanupDisplayWorkerCleanupTime_ /
278 inst.cleanupTime_ * 100.0,
279 5, 'f', 1)
280 .arg(inst.cleanupAppBridgeCleanupTime_, 9, 'f', 4)
281 .arg(inst.cleanupAppBridgeCleanupTime_ /
282 inst.cleanupTime_ * 100.0,
283 5, 'f', 1)
284 .arg(inst.cleanupAppManualDestroyTime_, 9, 'f', 4)
285 .arg(inst.cleanupAppManualDestroyTime_ /
286 inst.cleanupTime_ * 100.0,
287 5, 'f', 1)
288 .arg(inst.cleanupAppProcessEventsTime_, 9, 'f', 4)
289 .arg(inst.cleanupAppProcessEventsTime_ /
290 inst.cleanupTime_ * 100.0,
291 5, 'f', 1);
292 qDebug().noquote() << cleanupMsg;
293
294 if (inst.cleanupAppManualDestroyTime_ > 1e-9) {
295 const QString manualDestroyMsg =
296 QString(
297 "\n--- Manual Destroy Breakdown ---\nDetach "
298 "Container: %1s (%2%%)\nRelease Display: %3s "
299 "(%4%%)\nContainer Delete: %5s "
300 "(%6%%)\n------------------------")
301 .arg(inst.cleanupAppDetachContainerTime_, 9, 'f', 4)
302 .arg(inst.cleanupAppDetachContainerTime_ /
303 inst.cleanupAppManualDestroyTime_ * 100.0,
304 5, 'f', 1)
305 .arg(inst.cleanupAppReleaseDisplayTime_, 9, 'f', 4)
306 .arg(inst.cleanupAppReleaseDisplayTime_ /
307 inst.cleanupAppManualDestroyTime_ * 100.0,
308 5, 'f', 1)
309 .arg(inst.cleanupAppContainerDeleteScheduleTime_, 9,
310 'f', 4)
311 .arg(inst.cleanupAppContainerDeleteScheduleTime_ /
312 inst.cleanupAppManualDestroyTime_ * 100.0,
313 5, 'f', 1);
314 qDebug().noquote() << manualDestroyMsg;
315 }
316 }
317 }
318
319 /**
320 * \brief Resets all profiling data.
321 */
322 static void reset() {
323 auto& inst = instance();
324 inst.qtInitTime_ = 0.0;
325 inst.windowCreationTime_ = 0.0;
326 inst.eventLoopTime_ = 0.0;
327 inst.frameRenderTime_ = 0.0;
328 inst.cleanupTime_ = 0.0;
329 inst.cleanupDisplayPrepTime_ = 0.0;
330 inst.cleanupDisplayProcessEventsTime_ = 0.0;
331 inst.cleanupDisplayThreadStopTime_ = 0.0;
332 inst.cleanupDisplayWorkerCleanupTime_ = 0.0;
333 inst.cleanupAppBridgeCleanupTime_ = 0.0;
334 inst.cleanupAppManualDestroyTime_ = 0.0;
335 inst.cleanupAppProcessEventsTime_ = 0.0;
336 inst.cleanupAppDetachContainerTime_ = 0.0;
337 inst.cleanupAppReleaseDisplayTime_ = 0.0;
338 inst.cleanupAppContainerDeleteScheduleTime_ = 0.0;
339 inst.frameRenderCount_ = 0;
340 inst.qtInitStart_ = TimePoint{};
341 inst.windowCreationStart_ = TimePoint{};
342 inst.eventLoopStart_ = TimePoint{};
343 inst.frameRenderStart_ = TimePoint{};
344 inst.cleanupStart_ = TimePoint{};
345 }
346
347 private:
348 AppProfiler() = default;
349 ~AppProfiler() = default;
350 AppProfiler(const AppProfiler&) = delete;
351 AppProfiler& operator=(const AppProfiler&) = delete;
352
353 static AppProfiler& instance() {
354 static AppProfiler inst;
355 return inst;
356 }
357
358 double qtInitTime_ = 0.0;
359 double windowCreationTime_ = 0.0;
360 double eventLoopTime_ = 0.0;
361 double frameRenderTime_ = 0.0;
362 double cleanupTime_ = 0.0;
363 double cleanupDisplayPrepTime_ = 0.0;
364 double cleanupDisplayProcessEventsTime_ = 0.0;
365 double cleanupDisplayThreadStopTime_ = 0.0;
366 double cleanupDisplayWorkerCleanupTime_ = 0.0;
367 double cleanupAppBridgeCleanupTime_ = 0.0;
368 double cleanupAppManualDestroyTime_ = 0.0;
369 double cleanupAppProcessEventsTime_ = 0.0;
370 double cleanupAppDetachContainerTime_ = 0.0;
371 double cleanupAppReleaseDisplayTime_ = 0.0;
372 double cleanupAppContainerDeleteScheduleTime_ = 0.0;
373 std::size_t frameRenderCount_ = 0;
374
375 TimePoint qtInitStart_{};
376 TimePoint windowCreationStart_{};
377 TimePoint eventLoopStart_{};
378 TimePoint frameRenderStart_{};
379 TimePoint cleanupStart_{};
380 };
381} // namespace AppUtils
382
383#endif // APP_PROFILER_HPP
Global application profiler for measuring non-physics phases.
static void addCleanupDisplayProcessEvents(double seconds)
Adds measured time spent in Display cleanup processEvents.
static void addCleanupAppProcessEvents(double seconds)
Adds measured time spent in AppManager cleanup processEvents.
static void startQtInit()
Starts timing Qt initialization.
static void stopQtInit()
Stops timing Qt initialization.
static void addCleanupAppManualDestroy(double seconds)
Adds measured time for manual destroy operations.
static void startEventLoop()
Starts timing the Qt event loop.
static void printReport()
Prints the profiling report.
static void addCleanupDisplayPrep(double seconds)
Adds measured time for Display cleanup preparation.
static void addCleanupDisplayWorkerCleanup(double seconds)
Adds measured time for physics worker cleanup.
static void stopFrameRender()
Stops timing a frame render.
static void addCleanupAppBridgeCleanup(double seconds)
Adds measured time spent in bridge/display cleanup call.
static void addCleanupAppDetachContainer(double seconds)
Adds measured time for detaching the display container.
static void startFrameRender()
Starts timing a frame render.
static void stopEventLoop()
Stops timing the Qt event loop.
static void reset()
Resets all profiling data.
static void startCleanup()
Starts timing cleanup.
static void addCleanupAppContainerDeleteSchedule(double seconds)
Adds measured time for container delete action.
static void stopWindowCreation()
Stops timing 3D window creation.
static void addCleanupDisplayThreadStop(double seconds)
Adds measured time for stopping the physics thread.
static void addCleanupAppReleaseDisplay(double seconds)
Adds measured time for explicit display release.
static void startWindowCreation()
Starts timing 3D window creation.
static void stopCleanup()
Stops timing cleanup.
Namespace for application-wide utility functions, primarily for exception handling.