You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This section describes the profiling system introduced in `MGIS`, which is meant to:
22
+
23
+
1. provide a hierarchical and precise measurement of execution times across the library.
24
+
2. ensure minimal to zero overhead when disabled, preserving the high-performance computing requirements of the code.
25
+
26
+
By tying the profiling system directly to the `Context` object, `MGIS` avoids the need to pass an additional profiler object through the call stack. A function using this profiling strategy is recognizable by the presence of a `Context` object and the use of dedicated profiling macros.
27
+
28
+
# The `ProfilingData` structure
29
+
30
+
The result of the profiling is not a flat list of timers, but a hierarchical tree. Each node in this tree is represented by the `ProfilingData` structure, which stores:
31
+
32
+
-`name`: A `std::string` representing the name of the profiled section.
33
+
-`time_in_seconds`: A `double` accumulating the total time spent in this section.
34
+
-`calls`: An `unsigned int` counting the number of times this section was executed.
35
+
-`children`: A `std::vector` of `std::shared_ptr<ProfilingData>`, representing the nested profiled sections called within the current one.
36
+
37
+
# Managing the Profiling State
38
+
39
+
The profiling system is disabled by default to guarantee zero overhead in production runs. It can be dynamically controlled through the `Context` object using the following methods:
40
+
41
+
-`enableProfiling(bool)`: Turns the global profiling on or off for the given context.
42
+
-`isProfilingEnabled()`: Returns a boolean indicating the current state of the profiler.
43
+
-`getProfilingResultTree()`: Retrieves the root node of the profiling tree (`ProfilingData`) containing all the collected metrics.
44
+
45
+
# Instrumenting the code
46
+
47
+
The profiling relies on the **RAII** (Resource Acquisition Is Initialization) idiom. Instead of manually starting and stopping timers, the developer creates a scoped object that starts a timer upon construction and stops it, while updating the tree, upon destruction.
48
+
49
+
To ensure high readability and ease of use, this mechanism is wrapped in macros.
50
+
51
+
## The `CatchTimeSection` macro
52
+
53
+
The `CatchTimeSection` macro is the standard way to profile a block of code. It takes two arguments:
54
+
1. The `Context` object.
55
+
2. A string literal representing the name of the section.
56
+
57
+
If profiling is enabled in the provided `Context`, the macro will automatically find its place in the hierarchical tree, start the timer, and record the elapsed time at the end of the current scope. If profiling is disabled, the macro does nothing.
58
+
59
+
## The `CatchLocalTimeSection` macro
60
+
61
+
In some specific debugging scenarios, a developer might want to force the profiling of a specific section even if the global profiling state is disabled. The `CatchLocalTimeSection` macro takes a third boolean argument:
62
+
63
+
~~~~{.cxx}
64
+
// The third argument 'true' forces the profiling of this specific scope
To prevent the profiling tree from growing indefinitely and consuming too much memory, the system automatically aggregates repeated calls to the same section within the same parent scope.
71
+
72
+
If a profiled section is called multiple times (for example, inside a `for` or `while` loop), the profiler does not create a new child node for each iteration. Instead, it finds the existing child node with the same name, increments its `calls` counter, and adds the elapsed time to `time_in_seconds`.
73
+
74
+
# Example of usage
75
+
76
+
The following code illustrates how to instrument a function and how the tree hierarchy and loop aggregation behave:
77
+
78
+
~~~~{.cxx}
79
+
void performComputation(mgis::Context& ctx)
80
+
{
81
+
ctx.enableProfiling(true);
82
+
83
+
// Start a root section
84
+
{
85
+
CatchTimeSection(ctx, "IntegrationStep");
86
+
87
+
// Nested section
88
+
{
89
+
CatchTimeSection(ctx, "Initialization");
90
+
// ... initialization code ...
91
+
}
92
+
93
+
// Loop with a nested section
94
+
for (int i = 0; i < 1000; ++i) {
95
+
CatchTimeSection(ctx, "NewtonRaphsonIteration");
96
+
// ... solver code ...
97
+
}
98
+
}
99
+
100
+
// Retrieve and analyze the results
101
+
const auto& root = ctx.getProfilingResultTree();
102
+
// 'root' contains 1 child: "IntegrationStep"
103
+
// "IntegrationStep" contains 2 children: "Initialization" and "NewtonRaphsonIteration"
104
+
// "NewtonRaphsonIteration" will show: calls = 1000
105
+
}
106
+
~~~~
107
+
108
+
In this example, despite the `NewtonRaphsonIteration` section being timed 1000 times, only one node is created in the tree under `IntegrationStep`, with its `calls` attribute set to `1000` and its `time_in_seconds` representing the total time spent across all iterations.
109
+
110
+
> **Note**
111
+
>
112
+
> Exhaustive examples and unit tests of the profiling system, including edge cases, can be found in the `tests/ProfilingTest.cxx` file.
0 commit comments