Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
performance_measurements.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <algorithm>
10#include <array>
11#include <atomic>
12#include <chrono>
13#include <concepts>
14#include <cstddef>
15#include <exception>
16#include <functional>
17#include <optional>
18#include <ranges>
19#include <span>
20#include <type_traits>
21#include <utility>
22#include <vector>
23
25{
26
29{
30 none,
31 coarse,
33};
34
42
44using Duration = std::chrono::nanoseconds;
45
47using WallClock = std::chrono::steady_clock;
48
51{
53 std::size_t count = 0;
55 Duration total = Duration::zero();
57 Duration minimum = Duration::zero();
59 Duration maximum = Duration::zero();
60
63 [[nodiscard]] constexpr auto mean() const noexcept -> Duration
64 {
65 return count == 0 ? Duration::zero() : total / static_cast<Duration::rep>(count);
66 }
67};
68
76template <class Event, std::size_t EventCount> class DurationMeasurements {
77 public:
79 static constexpr std::size_t event_count = EventCount;
80
85 void record_duration(Event event, Duration duration) noexcept
86 {
87 auto& statistics = values_[event_index(event)];
88 if (statistics.count == 0)
89 {
90 statistics.minimum = duration;
91 statistics.maximum = duration;
92 }
93 else
94 {
95 statistics.minimum = std::min(statistics.minimum, duration);
96 statistics.maximum = std::max(statistics.maximum, duration);
97 }
98 ++statistics.count;
99 statistics.total += duration;
100 }
101
106 [[nodiscard]] auto operator[](Event event) const noexcept -> DurationStatistics const&
107 {
108 return values_[event_index(event)];
109 }
110
112 constexpr void reset() noexcept { values_ = {}; }
113
114 private:
115 [[nodiscard]] static constexpr auto event_index(Event event) noexcept -> std::size_t
116 {
117 return static_cast<std::size_t>(event);
118 }
119
120 std::array<DurationStatistics, EventCount> values_{};
121};
122
125{
127 std::size_t requested_items = 0;
129 std::size_t started_items = 0;
131 std::size_t completed_items = 0;
133 std::size_t peak_concurrency = 0;
135 Duration wall_duration = Duration::zero();
137 Duration total_item_duration = Duration::zero();
139 Duration maximum_item_duration = Duration::zero();
143 Duration item_start_spread = Duration::zero();
145 Duration item_finish_spread = Duration::zero();
148};
149
156template <class Event, std::size_t EventCount>
157class DetailedMeasurements : public DurationMeasurements<Event, EventCount> {
158 public:
160
165 void record_batch(Event event, BatchMeasurement measurement)
166 {
167 batches_[static_cast<std::size_t>(event)].push_back(std::move(measurement));
168 }
169
174 [[nodiscard]] auto batches(Event event) const noexcept -> std::span<BatchMeasurement const>
175 {
176 return batches_[static_cast<std::size_t>(event)];
177 }
178
180 void reset()
181 {
183 batches_ = {};
184 }
185
186 private:
187 std::array<std::vector<BatchMeasurement>, EventCount> batches_{};
188};
189
191template <class Measurements>
192inline constexpr MeasurementLevel measurement_level_v = std::remove_cvref_t<Measurements>::measurement_level;
193
195template <class Measurements, class Event>
196concept DurationMeasurementPolicy = requires { std::remove_cvref_t<Measurements>::measurement_level; } &&
197 (measurement_level_v<Measurements> == MeasurementLevel::none ||
198 requires(Measurements& measurements, Event event, Duration duration) {
199 { measurements.record_duration(event, duration) } noexcept -> std::same_as<void>;
200 });
201
203template <class Measurements, class Event>
205 (measurement_level_v<Measurements> != MeasurementLevel::detailed ||
206 requires(Measurements& measurements, Event event, BatchMeasurement batch) {
207 { measurements.record_batch(event, std::move(batch)) } -> std::same_as<void>;
208 });
209
210namespace detail
211{
212
213template <class Measurements, class Event> class DurationGuard {
214 public:
215 DurationGuard(Measurements& measurements, Event event) noexcept
216 : measurements_(measurements), event_(event), start_(WallClock::now())
217 {}
218
219 DurationGuard(DurationGuard const&) = delete;
220 DurationGuard(DurationGuard&&) = delete;
221 auto operator=(DurationGuard const&) -> DurationGuard& = delete;
222 auto operator=(DurationGuard&&) -> DurationGuard& = delete;
223
224 ~DurationGuard() noexcept
225 {
226 auto const elapsed = std::chrono::duration_cast<Duration>(WallClock::now() - start_);
227 measurements_.record_duration(event_, elapsed);
228 }
229
230 private:
231 Measurements& measurements_;
232 Event event_;
233 WallClock::time_point start_;
234};
235
236} // namespace detail
237
249template <class Measurements, class Event, class Function>
250 requires DurationMeasurementPolicy<Measurements, Event> && std::invocable<Function>
251decltype(auto) measure_duration(Measurements& measurements, Event event, Function&& function)
252{
253 if constexpr (measurement_level_v<Measurements> == MeasurementLevel::none)
254 {
255 static_cast<void>(measurements);
256 static_cast<void>(event);
257 return std::invoke(std::forward<Function>(function));
258 }
259 else
260 {
261 detail::DurationGuard guard(measurements, event);
262 return std::invoke(std::forward<Function>(function));
263 }
264}
265
266namespace detail
267{
268
269struct BatchItemInterval
270{
271 bool started = false;
272 bool completed = false;
273 WallClock::time_point start = {};
274 WallClock::time_point finish = {};
275 std::size_t start_order = 0;
276 std::size_t finish_order = 0;
277};
278
279inline auto summarize_batch(std::span<BatchItemInterval const> items, WallClock::time_point batch_start,
280 WallClock::time_point batch_finish) -> BatchMeasurement
281{
282 BatchMeasurement result{.requested_items = items.size(),
283 .wall_duration = std::chrono::duration_cast<Duration>(batch_finish - batch_start)};
284 using boundary_type = std::pair<std::size_t, int>;
285 std::vector<boundary_type> boundaries;
286 boundaries.reserve(items.size() * 2);
287 std::optional<WallClock::time_point> first_start;
288 std::optional<WallClock::time_point> last_start;
289 std::optional<WallClock::time_point> first_finish;
290 std::optional<WallClock::time_point> last_finish;
291 for (auto const& item : items)
292 {
293 if (!item.started) continue;
294 ++result.started_items;
295 first_start = first_start ? std::min(*first_start, item.start) : item.start;
296 last_start = last_start ? std::max(*last_start, item.start) : item.start;
297 if (!item.completed) continue;
298 ++result.completed_items;
299 first_finish = first_finish ? std::min(*first_finish, item.finish) : item.finish;
300 last_finish = last_finish ? std::max(*last_finish, item.finish) : item.finish;
301 auto const duration = std::chrono::duration_cast<Duration>(item.finish - item.start);
302 result.total_item_duration += duration;
303 result.maximum_item_duration = std::max(result.maximum_item_duration, duration);
304 boundaries.emplace_back(item.start_order, 1);
305 boundaries.emplace_back(item.finish_order, -1);
306 }
307
308 if (first_start)
309 {
310 result.first_item_start_delay = std::chrono::duration_cast<Duration>(*first_start - batch_start);
311 result.item_start_spread = std::chrono::duration_cast<Duration>(*last_start - *first_start);
312 }
313 if (first_finish)
314 {
315 result.item_finish_spread = std::chrono::duration_cast<Duration>(*last_finish - *first_finish);
316 result.return_after_last_finish = std::chrono::duration_cast<Duration>(batch_finish - *last_finish);
317 }
318
319 std::ranges::sort(boundaries, {}, &boundary_type::first);
320 std::size_t active = 0;
321 for (auto const& [order, delta] : boundaries)
322 {
323 static_cast<void>(order);
324 if (delta < 0)
325 --active;
326 else
327 result.peak_concurrency = std::max(result.peak_concurrency, ++active);
328 }
329 return result;
330}
331
332} // namespace detail
333
352template <class Measurements, class Event, class Executor, class Function>
353 requires BatchMeasurementPolicy<Measurements, Event> && std::invocable<Function&, std::size_t>
354void measure_batch(Measurements& measurements, Event event, std::size_t size, Executor&& executor, Function&& function)
355{
356 if constexpr (measurement_level_v<Measurements> == MeasurementLevel::none)
357 {
358 static_cast<void>(measurements);
359 static_cast<void>(event);
360 std::invoke(std::forward<Executor>(executor), function);
361 }
362 else if constexpr (measurement_level_v<Measurements> == MeasurementLevel::coarse)
363 {
364 measure_duration(measurements, event, [&] { std::invoke(std::forward<Executor>(executor), function); });
365 }
366 else
367 {
368 std::vector<detail::BatchItemInterval> items(size);
369 std::atomic_size_t next_boundary_order = 0;
370 auto wrapped = [&](std::size_t index) {
371 auto& item = items[index];
372 item.started = true;
373 item.start_order = next_boundary_order.fetch_add(1, std::memory_order_relaxed);
374 item.start = WallClock::now();
375 try
376 {
377 std::invoke(function, index);
378 }
379 catch (...)
380 {
381 item.finish = WallClock::now();
382 item.finish_order = next_boundary_order.fetch_add(1, std::memory_order_relaxed);
383 item.completed = true;
384 throw;
385 }
386 item.finish = WallClock::now();
387 item.finish_order = next_boundary_order.fetch_add(1, std::memory_order_relaxed);
388 item.completed = true;
389 };
390
391 auto const start = WallClock::now();
392 std::exception_ptr failure;
393 try
394 {
395 std::invoke(std::forward<Executor>(executor), wrapped);
396 }
397 catch (...)
398 {
399 failure = std::current_exception();
400 }
401 auto const finish = WallClock::now();
402 auto batch = detail::summarize_batch(items, start, finish);
403 measurements.record_duration(event, batch.wall_duration);
404 measurements.record_batch(event, std::move(batch));
405 if (failure) std::rethrow_exception(failure);
406 }
407}
408
409} // namespace uni20::performance
Fixed-size detailed collector retaining every measured batch.
Definition performance_measurements.hpp:157
void record_batch(Event event, BatchMeasurement measurement)
Retain one completed synchronous batch measurement.
Definition performance_measurements.hpp:165
void reset()
Clear duration aggregates and retained batch measurements.
Definition performance_measurements.hpp:180
auto batches(Event event) const noexcept -> std::span< BatchMeasurement const >
Return detailed batch records for one event.
Definition performance_measurements.hpp:174
static constexpr MeasurementLevel measurement_level
Definition performance_measurements.hpp:159
Fixed-size coarse wall-duration collector indexed by an event enum.
Definition performance_measurements.hpp:76
static constexpr std::size_t event_count
Definition performance_measurements.hpp:79
auto operator[](Event event) const noexcept -> DurationStatistics const &
Return aggregate statistics for one event.
Definition performance_measurements.hpp:106
constexpr void reset() noexcept
Clear every recorded statistic.
Definition performance_measurements.hpp:112
static constexpr MeasurementLevel measurement_level
Definition performance_measurements.hpp:78
void record_duration(Event event, Duration duration) noexcept
Record one completed event duration.
Definition performance_measurements.hpp:85
Measurement policy compatible with detailed synchronous batch records.
Definition performance_measurements.hpp:204
Measurement policy that either disables instrumentation or records event durations.
Definition performance_measurements.hpp:196
Definition performance_measurements.hpp:25
MeasurementLevel
Selects how much performance instrumentation an operation instantiates.
Definition performance_measurements.hpp:29
void measure_batch(Measurements &measurements, Event event, std::size_t size, Executor &&executor, Function &&function)
Execute a synchronous batch with compile-time-selected timing detail.
Definition performance_measurements.hpp:354
std::chrono::nanoseconds Duration
Duration type shared by performance measurement collectors.
Definition performance_measurements.hpp:44
decltype(auto) measure_duration(Measurements &measurements, Event event, Function &&function)
Invoke a function with compile-time-gated wall-duration measurement.
Definition performance_measurements.hpp:251
constexpr MeasurementLevel measurement_level_v
Compile-time instrumentation level declared by a measurement policy.
Definition performance_measurements.hpp:192
std::chrono::steady_clock WallClock
Clock used for host wall-duration measurements.
Definition performance_measurements.hpp:47
Detailed timing summary for one synchronous lightweight batch.
Definition performance_measurements.hpp:125
Duration maximum_item_duration
Largest completed item duration.
Definition performance_measurements.hpp:139
Duration total_item_duration
Sum of completed item durations.
Definition performance_measurements.hpp:137
Duration return_after_last_finish
Delay from the last item finish until the batch executor returned.
Definition performance_measurements.hpp:147
std::size_t peak_concurrency
Largest number of item invocations entered but not yet exited.
Definition performance_measurements.hpp:133
std::size_t completed_items
Number of started item invocations which exited, including by exception.
Definition performance_measurements.hpp:131
std::size_t started_items
Number of items whose invocation began.
Definition performance_measurements.hpp:129
Duration item_finish_spread
Difference between the first and last item finish times.
Definition performance_measurements.hpp:145
Duration first_item_start_delay
Delay from batch entry until the first item began.
Definition performance_measurements.hpp:141
Duration item_start_spread
Difference between the first and last item start times.
Definition performance_measurements.hpp:143
Duration wall_duration
Duration from entry to the batch executor until its return or exception.
Definition performance_measurements.hpp:135
std::size_t requested_items
Number of items requested from the scheduler.
Definition performance_measurements.hpp:127
Aggregate duration statistics for repeated instances of one event.
Definition performance_measurements.hpp:51
Duration maximum
Largest recorded duration, or zero when no instance was recorded.
Definition performance_measurements.hpp:59
Duration total
Sum of all recorded durations.
Definition performance_measurements.hpp:55
std::size_t count
Number of recorded event instances.
Definition performance_measurements.hpp:53
constexpr auto mean() const noexcept -> Duration
Return the arithmetic mean duration.
Definition performance_measurements.hpp:63
Duration minimum
Smallest recorded duration, or zero when no instance was recorded.
Definition performance_measurements.hpp:57
Explicit disabled measurement policy used by ordinary operation overloads.
Definition performance_measurements.hpp:39
static constexpr MeasurementLevel measurement_level
Definition performance_measurements.hpp:40