/****************************************************************************** * Top contributors (to current version): * Gereon Kremer * * This file is part of the cvc5 project. * * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS * in the top-level source directory and their institutional affiliations. * All rights reserved. See the file COPYING in the top-level source * directory for licensing information. * **************************************************************************** * * Central statistics registry. * * The StatisticsRegistry that issues statistic proxy objects. */ /** * On the design of the statistics: * * Stat is the abstract base class for all statistic values. * It stores the name and provides (fully virtual) methods * flushInformation() and safeFlushInformation(). * * BackedStat is an abstract templated base class for statistic values * that store the data themselves. It takes care of printing them already * and derived classes usually only need to provide methods to set the * value. * * ReferenceStat holds a reference (conceptually, it is implemented as a * const pointer) to some data that is stored outside of the statistic. * * IntStat is a BackedStat. * * SizeStat holds a const reference to some container and provides the * size of this container. * * AverageStat is a BackedStat. * * HistogramStat counts instances of some type T. It is implemented as a * std::map. * * IntegralHistogramStat is a (conceptual) specialization of HistogramStat * for types that are (convertible to) integral. This allows to use a * std::vector instead of a std::map. * * TimerStat uses std::chrono to collect timing information. It is * implemented as BackedStat and provides methods * start() and stop(), accumulating times it was activated. It provides * the convenience class CodeTimer to allow for RAII-style usage. * * * All statistic classes should protect their custom methods using * if (CVC5_USE_STATISTICS) { ... } * Output methods (flushInformation() and safeFlushInformation()) are only * called when statistics are enabled and need no protection. * * * The statistic classes try to implement a consistent interface: * - if we store some generic data, we implement set() * - if we (conceptually) store a set of values, we implement operator<<() * - if there are standard operations for the stored data, we implement these * (like operator++() or operator+=()) */ #include "cvc5_private_library.h" #ifndef CVC5__STATISTICS_REGISTRY_H #define CVC5__STATISTICS_REGISTRY_H #include #include #include #include #include "base/check.h" #include "util/statistics_stats.h" #include "util/statistics_value.h" namespace cvc5 { struct StatisticBaseValue; /** * The central registry for statistics. * Internally stores statistic data objects and issues corresponding proxy * objects to modules that want to expose statistics. * Provides registration methods (e.g. `registerAverage` or * `registerHistogram`) that return the proxy object. * The different statistics are explained in more detail in statistics_stats.h * * Every statistic is identified by a name. If a statistic with the given * name is already registered, the registry issues another proxy object * for that name using the same data it already holds for this name. * While this makes perfect sense for most statistic types, it may lead to * unexpected (though not undefined) behaviour for others. For a reference * statistic, for example, the internal data will simply point to the object * registered last. * Note that the type of the re-registered statistic must always match * the type of the previously registered statistic with the same name. * * We generally distinguish between public (non-expert) and private (expert) * statistics. By default, `--stats` only shows public statistics. Private * ones are printed as well if `--all-statistics` is set. * All registration methods have a trailing argument `expert`, defaulting to * true. * * If statistics are disabled entirely (i.e. the cmake option * `ENABLE_STATISTICS` is not set), the registry still issues proxy objects * that can be used normally. * However, no data is stored in the registry and the modification functions * of the proxy objects do nothing. */ class StatisticsRegistry { public: friend std::ostream& operator<<(std::ostream& os, const StatisticsRegistry& sr); using Snapshot = std::map; /** * If `registerPublic` is true, all statistics that are public are * pre-registered as such. This argument mostly exists so that unit tests * can disable this pre-registration. */ StatisticsRegistry(bool registerPublic = true); /** Register a new running average statistic for `name` */ AverageStat registerAverage(const std::string& name, bool expert = true); /** Register a new histogram statistic for `name` */ template HistogramStat registerHistogram(const std::string& name, bool expert = true) { return registerStat>(name, expert); } /** Register a new integer statistic for `name` */ IntStat registerInt(const std::string& name, bool expert = true); /** Register a new reference statistic for `name` */ template ReferenceStat registerReference(const std::string& name, bool expert = true) { return registerStat>(name, expert); } /** * Register a new reference statistic for `name` and initialize it to * refer to `t`. */ template ReferenceStat registerReference(const std::string& name, const T& t, bool expert = true) { ReferenceStat res = registerStat>(name, expert); res.set(t); return res; } /** * Register a new container size statistic for `name` and initialize it * to refer to `t`. */ template SizeStat registerSize(const std::string& name, const T& t, bool expert = true) { SizeStat res = registerStat>(name, expert); res.set(t); return res; } /** Register a new timer statistic for `name` */ TimerStat registerTimer(const std::string& name, bool expert = true); /** Register a new value statistic for `name`. */ template ValueStat registerValue(const std::string& name, bool expert = true) { return registerStat>(name, expert); } /** Register a new value statistic for `name` and set it to `init`. */ template ValueStat registerValue(const std::string& name, const T& init, bool expert = true) { ValueStat res = registerStat>(name, expert); res.set(init); return res; } /** begin iteration */ auto begin() const { return d_stats.begin(); } /** end iteration */ auto end() const { return d_stats.end(); } /** * Obtain the current state of all statistics. */ void storeSnapshot(); /** * Obtain a single statistic by name. Returns nullptr if no statistic has * been registered for this name. */ StatisticBaseValue* get(const std::string& name) const; /** * Print all statistics to the given output stream. */ void print(std::ostream& os) const; /** * Print all statistics in a safe manner to the given file descriptor. */ void printSafe(int fd) const; /** * Print all statistics as a diff to the last stored snapshot. */ void printDiff(std::ostream& os) const; private: /** * Helper method to register a new statistic. * If the name was already used, a new proxy object is created. * We check whether the type matches the type of the originally registered * statistic using `typeid`. */ template Stat registerStat(const std::string& name, bool expert) { if constexpr (Configuration::isStatisticsBuild()) { auto it = d_stats.find(name); if (it == d_stats.end()) { it = d_stats.emplace(name, std::make_unique()) .first; it->second->d_expert = expert; } auto* ptr = it->second.get(); Assert(typeid(*ptr) == typeid(typename Stat::stat_type)) << "Statistic value " << name << " was registered again with a different type."; it->second->d_expert = it->second->d_expert && expert; return Stat(static_cast(ptr)); } return Stat(nullptr); } /** * Holds (and owns) all statistic values, indexed by the name they were * registered for. */ std::map> d_stats; std::unique_ptr d_lastSnapshot; }; /** Calls `sr.print(os)`. */ std::ostream& operator<<(std::ostream& os, const StatisticsRegistry& sr); } // namespace cvc5 #endif /* CVC5__STATISTICS_REGISTRY_H */