LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/util - statistics_value.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 58 87 66.7 %
Date: 2025-03-23 12:53:24 Functions: 52 86 60.5 %
Branches: 31 54 57.4 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Gereon Kremer, Andrew Reynolds, Aina Niemetz
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Statistic data classes.
      14                 :            :  *
      15                 :            :  * The statistic data classes that actually hold the data for the statistics.
      16                 :            :  *
      17                 :            :  * Conceptually, every statistic consists of a data object and a proxy object.
      18                 :            :  * The data objects (statistic values) are derived from `StatisticBaseValue`
      19                 :            :  * and live in the `StatisticsRegistry`.
      20                 :            :  * They are solely exported to the proxy objects, which should be the sole
      21                 :            :  * way to manipulate the data of a data object.
      22                 :            :  * The data objects themselves need to implement printing (normal and safe) and
      23                 :            :  * conversion to the API type `Stat`.
      24                 :            :  */
      25                 :            : 
      26                 :            : #include "cvc5_private_library.h"
      27                 :            : 
      28                 :            : #ifndef CVC5__UTIL__STATISTICS_VALUE_H
      29                 :            : #define CVC5__UTIL__STATISTICS_VALUE_H
      30                 :            : 
      31                 :            : #include <chrono>
      32                 :            : #include <iomanip>
      33                 :            : #include <map>
      34                 :            : #include <optional>
      35                 :            : #include <sstream>
      36                 :            : #include <variant>
      37                 :            : #include <vector>
      38                 :            : 
      39                 :            : #include "util/safe_print.h"
      40                 :            : 
      41                 :            : namespace cvc5::internal {
      42                 :            : 
      43                 :            : class StatisticsRegistry;
      44                 :            : 
      45                 :            : using StatExportData =
      46                 :            :     std::variant<int64_t, double, std::string, std::map<std::string, uint64_t>>;
      47                 :            : namespace detail {
      48                 :            :   std::ostream& print(std::ostream& out, const StatExportData& sed);
      49                 :            : }
      50                 :            : 
      51                 :            : /**
      52                 :            :  * Base class for all statistic values.
      53                 :            :  */
      54                 :            : struct StatisticBaseValue
      55                 :            : {
      56                 :            :   virtual ~StatisticBaseValue();
      57                 :            :   /** Checks whether the data holds the default value. */
      58                 :            :   virtual bool isDefault() const = 0;
      59                 :            :   /**
      60                 :            :    * Converts the internal data to an instance of `StatExportData` that is
      61                 :            :    * suitable for printing and exporting to the API.
      62                 :            :    */
      63                 :            :   virtual StatExportData getViewer() const = 0;
      64                 :            :   /**
      65                 :            :    * Safely writes the data to a file descriptor. Is suitable to be used
      66                 :            :    * within a signal handler.
      67                 :            :    */
      68                 :            :   virtual void printSafe(int fd) const = 0;
      69                 :            : 
      70                 :            :   bool d_internal = true;
      71                 :            : };
      72                 :            : /** Writes the data to an output stream */
      73                 :            : std::ostream& operator<<(std::ostream& out, const StatisticBaseValue& sbv);
      74                 :            : 
      75                 :            : /** Holds the data for an running average statistic */
      76                 :            : struct StatisticAverageValue : StatisticBaseValue
      77                 :            : {
      78                 :            :   StatExportData getViewer() const override;
      79                 :            :   bool isDefault() const override;
      80                 :            :   void printSafe(int fd) const override;
      81                 :            :   double get() const;
      82                 :            : 
      83                 :            :   /** Sum of added values */
      84                 :            :   double d_sum;
      85                 :            :   /** Number of added values */
      86                 :            :   uint64_t d_count;
      87                 :            : };
      88                 :            : 
      89                 :            : /**
      90                 :            :  * Holds some value of type `T`.
      91                 :            :  *
      92                 :            :  * To convert to the API representation in `getViewer`, `T` can only be one
      93                 :            :  * of the types listed in `Stat::d_data` (or be implicitly converted to
      94                 :            :  * one of them).
      95                 :            :  */
      96                 :            : template <typename T>
      97                 :            : struct StatisticBackedValue : StatisticBaseValue
      98                 :            : {
      99                 :      90940 :   StatExportData getViewer() const override { return d_value; }
     100                 :      90938 :   bool isDefault() const override { return d_value == T(); }
     101                 :          0 :   void printSafe(int fd) const override { safe_print<T>(fd, d_value); }
     102                 :            : 
     103                 :            :   T d_value;
     104                 :            : };
     105                 :            : 
     106                 :            : /**
     107                 :            :  * Holds the data for a histogram. We assume the type to be (convertible to)
     108                 :            :  * integral, and we can thus use a std::vector<uint64_t> for fast storage.
     109                 :            :  * The core idea is to track the minimum and maximum values `[a,b]` that have
     110                 :            :  * been added to the histogram and maintain a vector with `b-a+1` values.
     111                 :            :  * The vector is resized on demand to grow as necessary and supports negative
     112                 :            :  * values as well.
     113                 :            :  * Note that the template type needs to have a streaming operator to convert it
     114                 :            :  * to a string in `getViewer`.
     115                 :            :  */
     116                 :            : template <typename Integral>
     117                 :            : struct StatisticHistogramValue : StatisticBaseValue
     118                 :            : {
     119                 :            :   static_assert(std::is_integral<Integral>::value
     120                 :            :                     || std::is_enum<Integral>::value,
     121                 :            :                 "Type should be a fundamental integral type.");
     122                 :            : 
     123                 :            :   /**
     124                 :            :    * Convert the internal representation to a `std::map<std::string, uint64_t>`
     125                 :            :    */
     126                 :      16478 :   StatExportData getViewer() const override
     127                 :            :   {
     128                 :      32956 :     std::map<std::string, uint64_t> res;
     129         [ +  + ]:      20713 :     for (size_t i = 0, n = d_hist.size(); i < n; ++i)
     130                 :            :     {
     131         [ +  + ]:       4235 :       if (d_hist[i] > 0)
     132                 :            :       {
     133                 :       2068 :         std::stringstream ss;
     134                 :       2068 :         ss << static_cast<Integral>(i + d_offset);
     135                 :       2068 :         res.emplace(ss.str(), d_hist[i]);
     136                 :            :       }
     137                 :            :     }
     138                 :      32956 :     return res;
     139                 :            :   }
     140                 :      17057 :   bool isDefault() const override { return d_hist.size() == 0; }
     141                 :          2 :   void printSafe(int fd) const override
     142                 :            :   {
     143                 :          2 :     safe_print(fd, "{ ");
     144                 :          2 :     bool first = true;
     145         [ +  + ]:         11 :     for (size_t i = 0, n = d_hist.size(); i < n; ++i)
     146                 :            :     {
     147         [ +  + ]:          9 :       if (d_hist[i] > 0)
     148                 :            :       {
     149         [ +  + ]:          3 :         if (first)
     150                 :            :         {
     151                 :          2 :           first = false;
     152                 :            :         }
     153                 :            :         else
     154                 :            :         {
     155                 :          1 :           safe_print(fd, ", ");
     156                 :            :         }
     157                 :          3 :         safe_print<Integral>(fd, static_cast<Integral>(i + d_offset));
     158                 :          3 :         safe_print(fd, ": ");
     159                 :          3 :         safe_print<uint64_t>(fd, d_hist[i]);
     160                 :            :       }
     161                 :            :     }
     162                 :          2 :     safe_print(fd, " }");
     163                 :          2 :   }
     164                 :            : 
     165                 :            :   /**
     166                 :            :    * Add `val` to the histogram. Casts `val` to `int64_t`, then resizes and
     167                 :            :    * moves the vector entries as necessary.
     168                 :            :    */
     169                 :  209682497 :   void add(Integral val)
     170                 :            :   {
     171                 :  209682497 :     int64_t v = static_cast<int64_t>(val);
     172         [ +  + ]:  209682497 :     if (d_hist.empty())
     173                 :            :     {
     174                 :     233970 :       d_offset = v;
     175                 :            :     }
     176         [ +  + ]:  209682497 :     if (v < d_offset)
     177                 :            :     {
     178                 :     157699 :       d_hist.insert(d_hist.begin(), d_offset - v, 0);
     179                 :     157699 :       d_offset = v;
     180                 :            :     }
     181         [ +  + ]:  209682497 :     if (static_cast<size_t>(v - d_offset) >= d_hist.size())
     182                 :            :     {
     183                 :     488449 :       d_hist.resize(v - d_offset + 1);
     184                 :            :     }
     185                 :  209682497 :     d_hist[v - d_offset]++;
     186                 :  209682497 :   }
     187                 :            :   /** Get the value stored for key val */
     188                 :          0 :   uint64_t getValue(Integral val)
     189                 :            :   {
     190                 :          0 :     int64_t index = static_cast<int64_t>(val);
     191         [ -  - ]:          0 :     if (index < d_offset)
     192                 :            :     {
     193                 :          0 :       return 0;
     194                 :            :     }
     195                 :          0 :     index = index - d_offset;
     196         [ -  - ]:          0 :     return static_cast<size_t>(index) < d_hist.size() ? d_hist[index] : 0;
     197                 :            :   }
     198                 :            : 
     199                 :            :   /** Actual data */
     200                 :            :   std::vector<uint64_t> d_hist;
     201                 :            :   /** Offset of the entries. d_hist[i] corresponds to Integral(d_offset + i) */
     202                 :            :   int64_t d_offset;
     203                 :            : };
     204                 :            : 
     205                 :            : /**
     206                 :            :  * Holds the data for a `ReferenceStat`.
     207                 :            :  * When the `ReferenceStat` is destroyed the current value is copied into
     208                 :            :  * `d_committed`. Once `d_committed` is set, this value is returned, even if
     209                 :            :  * the reference is still valid.
     210                 :            :  */
     211                 :            : template <typename T>
     212                 :            : struct StatisticReferenceValue : StatisticBaseValue
     213                 :            : {
     214                 :       3710 :   StatExportData getViewer() const override
     215                 :            :   {
     216         [ -  + ]:       3710 :     if (d_committed)
     217                 :            :     {
     218                 :            :       if constexpr (std::is_integral_v<T>)
     219                 :            :       {
     220                 :          0 :         return static_cast<int64_t>(*d_committed);
     221                 :            :       }
     222                 :            :       else
     223                 :            :       {
     224                 :            :         // this else branch is required to ensure compilation.
     225                 :            :         // if T is unsigned int, this return statement triggers a compiler error
     226                 :          0 :         return *d_committed;
     227                 :            :       }
     228                 :            :     }
     229         [ +  - ]:       3710 :     else if (d_value != nullptr)
     230                 :            :     {
     231                 :            :       if constexpr (std::is_integral_v<T>)
     232                 :            :       {
     233                 :       3708 :         return static_cast<int64_t>(*d_value);
     234                 :            :       }
     235                 :            :       else
     236                 :            :       {
     237                 :            :         // this else branch is required to ensure compilation.
     238                 :            :         // if T is unsigned int, this return statement triggers a compiler error
     239                 :          2 :         return *d_value;
     240                 :            :       }
     241                 :            :     }
     242                 :            :     if constexpr (std::is_integral_v<T>)
     243                 :            :     {
     244                 :          0 :       return static_cast<int64_t>(0);
     245                 :            :     }
     246                 :            :     else
     247                 :            :     {
     248                 :            :       // this else branch is required to ensure compilation.
     249                 :            :       // if T is unsigned int, this return statement triggers a compiler error
     250                 :          0 :       return T();
     251                 :            :     }
     252                 :            :   }
     253                 :       3706 :   bool isDefault() const override
     254                 :            :   {
     255         [ -  + ]:       3706 :     if (d_committed)
     256                 :            :     {
     257                 :          0 :       return *d_committed == T();
     258                 :            :     }
     259 [ +  - ][ +  + ]:       3706 :     return d_value == nullptr || *d_value == T();
                 [ -  - ]
     260                 :            :   }
     261                 :          0 :   void printSafe(int fd) const override
     262                 :            :   {
     263         [ -  - ]:          0 :     if (d_committed)
     264                 :            :     {
     265                 :          0 :       safe_print<T>(fd, *d_committed);
     266                 :            :     }
     267         [ -  - ]:          0 :     else if (d_value != nullptr)
     268                 :            :     {
     269                 :          0 :       safe_print<T>(fd, *d_value);
     270                 :            :     }
     271                 :            :     else
     272                 :            :     {
     273                 :          0 :       safe_print<T>(fd, T());
     274                 :            :     }
     275                 :          0 :   }
     276                 :    1152021 :   void commit()
     277                 :            :   {
     278         [ +  + ]:    1152021 :     if (d_value != nullptr)
     279                 :            :     {
     280                 :     686595 :       d_committed = *d_value;
     281                 :            :     }
     282                 :    1152021 :   }
     283                 :            :   const T& get() const { return d_committed ? *d_committed : *d_value; }
     284                 :            : 
     285                 :            :   const T* d_value = nullptr;
     286                 :            :   std::optional<T> d_committed;
     287                 :            : };
     288                 :            : 
     289                 :            : /**
     290                 :            :  * Holds the data for a `SizeStat`.
     291                 :            :  * When the `SizeStat` is destroyed the current size is copied into
     292                 :            :  * `d_committed`. Once `d_committed` is set, this value is returned, even if
     293                 :            :  * the reference is still valid.
     294                 :            :  */
     295                 :            : template <typename T>
     296                 :            : struct StatisticSizeValue : StatisticBaseValue
     297                 :            : {
     298                 :        285 :   StatExportData getViewer() const override
     299                 :            :   {
     300         [ -  + ]:        285 :     if (d_committed)
     301                 :            :     {
     302                 :          0 :       return static_cast<int64_t>(*d_committed);
     303                 :            :     }
     304         [ +  - ]:        285 :     else if (d_value != nullptr)
     305                 :            :     {
     306                 :        285 :       return static_cast<int64_t>(d_value->size());
     307                 :            :     }
     308                 :          0 :     return static_cast<int64_t>(0);
     309                 :            :   }
     310                 :        285 :   bool isDefault() const override
     311                 :            :   {
     312         [ -  + ]:        285 :     if (d_committed)
     313                 :            :     {
     314                 :          0 :       return *d_committed == 0;
     315                 :            :     }
     316 [ +  - ][ +  + ]:        285 :     return d_value == nullptr || d_value->size() == 0;
     317                 :            :   }
     318                 :          0 :   void printSafe(int fd) const override
     319                 :            :   {
     320         [ -  - ]:          0 :     if (d_committed)
     321                 :            :     {
     322                 :          0 :       safe_print(fd, *d_committed);
     323                 :            :     }
     324         [ -  - ]:          0 :     else if (d_value != nullptr)
     325                 :            :     {
     326                 :          0 :       safe_print(fd, d_value->size());
     327                 :            :     }
     328                 :            :     else
     329                 :            :     {
     330                 :          0 :       safe_print(fd, 0);
     331                 :            :     }
     332                 :          0 :   }
     333                 :      51132 :   void commit()
     334                 :            :   {
     335         [ +  - ]:      51132 :     if (d_value != nullptr)
     336                 :            :     {
     337                 :      51132 :       d_committed = d_value->size();
     338                 :            :     }
     339                 :      51132 :   }
     340                 :            :   size_t get() const { return d_committed ? *d_committed : d_value->size(); }
     341                 :            : 
     342                 :            :   const T* d_value = nullptr;
     343                 :            :   std::optional<std::size_t> d_committed;
     344                 :            : };
     345                 :            : 
     346                 :            : /**
     347                 :            :  * Holds the data for a `TimerStat`.
     348                 :            :  * Uses `std::chrono` to obtain the current time, store a time point and sum up
     349                 :            :  * the total durations.
     350                 :            :  */
     351                 :            : struct StatisticTimerValue : StatisticBaseValue
     352                 :            : {
     353                 :            :   using clock = std::chrono::steady_clock;
     354                 :            :   using time_point = clock::time_point;
     355                 :            :   struct duration : public std::chrono::nanoseconds
     356                 :            :   {
     357                 :            :   };
     358                 :            :   /** Returns the number of milliseconds */
     359                 :            :   StatExportData getViewer() const override;
     360                 :            :   bool isDefault() const override;
     361                 :            :   /** Prints seconds in fixed-point format */
     362                 :            :   void printSafe(int fd) const override;
     363                 :            :   /**
     364                 :            :    * Returns the elapsed time in milliseconds.
     365                 :            :    * Make sure that we include the time of a currently running timer
     366                 :            :    */
     367                 :            :   uint64_t get() const;
     368                 :            : 
     369                 :            :   /**
     370                 :            :    * The cumulative duration of the timer so far. 
     371                 :            :    * Does not include a currently running timer, but `get()` takes care of this.
     372                 :            :    */
     373                 :            :   duration d_duration;
     374                 :            :   /**
     375                 :            :    * The start time of a currently running timer.
     376                 :            :    * May not be reset when the timer is stopped.
     377                 :            :    */
     378                 :            :   time_point d_start;
     379                 :            :   /** Whether a timer is running right now. */
     380                 :            :   bool d_running;
     381                 :            : };
     382                 :            : 
     383                 :            : }  // namespace cvc5::internal
     384                 :            : 
     385                 :            : #endif

Generated by: LCOV version 1.14