From 40534011e8e048bd0e864b60751d4ada797a5675 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Wed, 3 Jan 2024 19:09:27 +0000 Subject: [PATCH] u/live_stats: Add helper to do live statistics on nano-seconds durations --- src/xrt/auxiliary/util/CMakeLists.txt | 2 + src/xrt/auxiliary/util/u_live_stats.cpp | 117 ++++++++++++++++++++++++ src/xrt/auxiliary/util/u_live_stats.h | 106 +++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 src/xrt/auxiliary/util/u_live_stats.cpp create mode 100644 src/xrt/auxiliary/util/u_live_stats.h diff --git a/src/xrt/auxiliary/util/CMakeLists.txt b/src/xrt/auxiliary/util/CMakeLists.txt index ab452405b..d001fd969 100644 --- a/src/xrt/auxiliary/util/CMakeLists.txt +++ b/src/xrt/auxiliary/util/CMakeLists.txt @@ -61,6 +61,8 @@ add_library( u_json.hpp u_limited_unique_id.cpp u_limited_unique_id.h + u_live_stats.cpp + u_live_stats.h u_logging.c u_logging.h u_metrics.c diff --git a/src/xrt/auxiliary/util/u_live_stats.cpp b/src/xrt/auxiliary/util/u_live_stats.cpp new file mode 100644 index 000000000..c8b7b3a19 --- /dev/null +++ b/src/xrt/auxiliary/util/u_live_stats.cpp @@ -0,0 +1,117 @@ +// Copyright 2024, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Live stats tracking and printing. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#include "u_live_stats.h" + +#include +#include + + +/* + * + * Helper functions. + * + */ + +static void +print_as_ms(u_pp_delegate_t dg, uint64_t value_ns) +{ + uint64_t in_us = value_ns / 1000; + uint64_t in_ms = in_us / 1000; + uint64_t in_1_000_ms = in_ms / 1000; + uint64_t in_1_000_000_ms = in_1_000_ms / 1000; + + // Prints " M'TTT'###.FFFms" + + // " M'" + if (in_1_000_000_ms >= 1) { + u_pp(dg, " %" PRIu64 "'", in_1_000_000_ms); + } else { + // " M'" + u_pp(dg, " "); + } + + // "TTT'" + if (in_1_000_ms >= 1000) { + // Need to pad with zeros + u_pp(dg, "%03" PRIu64 "'", in_1_000_ms % 1000); + } else if (in_1_000_ms >= 1) { + // Pad with spaces, we need to write a number. + u_pp(dg, "%3" PRIu64 "'", in_1_000_ms); + } else { + // "TTT'" + u_pp(dg, " "); + } + + // "###" + if (in_ms >= 1000) { + // Need to pad with zeros + u_pp(dg, "%03" PRIu64, in_ms % 1000); + } else { + // Pad with spaces, always need a numbere here. + u_pp(dg, "%3" PRIu64, in_ms % 1000); + } + + // ".FFFms" + u_pp(dg, ".%03" PRIu64 "ms", in_us % 1000); +} + + +/* + * + * 'Exported' functions. + * + */ + +extern "C" void +u_ls_ns_get_and_reset(struct u_live_stats_ns *uls, uint64_t *out_median, uint64_t *out_mean, uint64_t *out_worst) +{ + uint32_t count = uls->value_count; + + if (count == 0) { + *out_median = 0; + *out_mean = 0; + *out_worst = 0; + return; + } + + std::sort(&uls->values[0], &uls->values[count]); + + uint64_t worst = uls->values[count - 1]; // Always greater then 0. + uint64_t median = uls->values[count / 2]; + + uint64_t mean = 0; + for (uint32_t i = 0; i < count; ++i) { + mean += uls->values[i] / count; + } + + uls->value_count = 0; + *out_median = median; + *out_mean = mean; + *out_worst = worst; +} + +extern "C" void +u_ls_ns_print_header(u_pp_delegate_t dg) +{ + // "xxxxYYYYzzzzWWWW M'TTT'###.FFFms M'TTT'###.FFFms M'TTT'###.FFFms" + u_pp(dg, " name median mean worst"); +} + +extern "C" void +u_ls_ns_print_and_reset(struct u_live_stats_ns *uls, u_pp_delegate_t dg) +{ + uint64_t median, mean, worst; + u_ls_ns_get_and_reset(uls, &median, &mean, &worst); + + u_pp(dg, "%16s", uls->name); + print_as_ms(dg, median); + print_as_ms(dg, mean); + print_as_ms(dg, worst); +} diff --git a/src/xrt/auxiliary/util/u_live_stats.h b/src/xrt/auxiliary/util/u_live_stats.h new file mode 100644 index 000000000..189cb12ce --- /dev/null +++ b/src/xrt/auxiliary/util/u_live_stats.h @@ -0,0 +1,106 @@ +// Copyright 2024, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Live stats tracking and printing. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ +#pragma once + +#include "xrt/xrt_compiler.h" + +#include "util/u_pretty_print.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * Number of chars for the name of the live stats. + * + * @ingroup aux_util + */ +#define U_LIVE_STATS_NAME_COUNT (16) + +/*! + * Max number of values that can be put into the trackers. + * + * @ingroup aux_util + */ +#define U_LIVE_STATS_VALUE_COUNT (1024) + +/*! + * Struct to do live statistic tracking and printing of nano-seconds values, + * used by amongst other the compositor pacing code. + * + * @ingroup aux_util + */ +struct u_live_stats_ns +{ + //! Small name used for printing. + char name[U_LIVE_STATS_NAME_COUNT]; + + //! Number of values currently in struct. + uint32_t value_count; + + //! The values that will be used to calculate statistics. + uint64_t values[U_LIVE_STATS_VALUE_COUNT]; +}; + +/*! + * Add a value to the live stats struct, returns true if the struct is full + * either before or after adding the value. + * + * @public @memberof u_live_stats_ns + * @ingroup aux_util + */ +static inline bool +u_ls_ns_add(struct u_live_stats_ns *uls, uint64_t value) +{ + if (uls->value_count >= ARRAY_SIZE(uls->values)) { + return true; + } + + uls->values[uls->value_count++] = value; + + if (uls->value_count >= ARRAY_SIZE(uls->values)) { + return true; + } + + return false; +} + +/*! + * Get the median, mean and worst of the current set of values, + * then reset the struct. + * + * @public @memberof u_live_stats_ns + */ +void +u_ls_ns_get_and_reset(struct u_live_stats_ns *uls, uint64_t *out_median, uint64_t *out_mean, uint64_t *out_worst); + +/*! + * Prints a header that looks nice before @ref u_ls_print_and_reset, + * adding details about columns. Doesn't include any newlines. + * + * @public @memberof u_live_stats_ns + */ +void +u_ls_ns_print_header(u_pp_delegate_t dg); + +/*! + * Prints the calculated values and resets the struct, can be used with + * @ref u_ls_ns_print_header to get a nice header to the values. Doesn't + * include any newlines. + * + * @public @memberof u_live_stats_ns + */ +void +u_ls_ns_print_and_reset(struct u_live_stats_ns *uls, u_pp_delegate_t dg); + + +#ifdef __cplusplus +} +#endif