LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/util - resource_manager.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 106 149 71.1 %
Date: 2026-02-05 12:22:59 Functions: 19 26 73.1 %
Branches: 49 94 52.1 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Gereon Kremer, Liana Hadarean, Mathias Preiner
       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                 :            :  * This file provides the ResourceManager class. It can be used to impose
      14                 :            :  * (cumulative and per-call) resource limits on the solver, as well as per-call
      15                 :            :  * time limits.
      16                 :            :  */
      17                 :            : #include "util/resource_manager.h"
      18                 :            : 
      19                 :            : #include <algorithm>
      20                 :            : #include <ostream>
      21                 :            : 
      22                 :            : #include "base/check.h"
      23                 :            : #include "base/listener.h"
      24                 :            : #include "base/output.h"
      25                 :            : #include "options/base_options.h"
      26                 :            : #include "options/option_exception.h"
      27                 :            : #include "options/options.h"
      28                 :            : #include "util/statistics_registry.h"
      29                 :            : 
      30                 :            : using namespace std;
      31                 :            : 
      32                 :            : namespace cvc5::internal {
      33                 :            : 
      34                 :    2088290 : bool WallClockTimer::on() const
      35                 :            : {
      36                 :            :   // default-constructed time points are at the respective epoch
      37                 :    2088290 :   return d_limit.time_since_epoch().count() != 0;
      38                 :            : }
      39                 :     178878 : void WallClockTimer::set(uint64_t millis)
      40                 :            : {
      41         [ +  + ]:     178878 :   if (millis == 0)
      42                 :            :   {
      43                 :            :     // reset / deactivate
      44                 :     177176 :     d_start = time_point();
      45                 :     177176 :     d_limit = time_point();
      46                 :            :   }
      47                 :            :   else
      48                 :            :   {
      49                 :            :     // set to now() + millis
      50                 :       1702 :     d_start = clock::now();
      51                 :       1702 :     d_limit = d_start + std::chrono::milliseconds(millis);
      52                 :            :   }
      53                 :     178878 : }
      54                 :     126155 : uint64_t WallClockTimer::elapsed() const
      55                 :            : {
      56         [ +  + ]:     126155 :   if (!on()) return 0;
      57                 :            :   // now() - d_start casted to milliseconds
      58                 :       3414 :   return std::chrono::duration_cast<std::chrono::milliseconds>(clock::now()
      59                 :       1707 :                                                                - d_start)
      60                 :       1707 :       .count();
      61                 :            : }
      62                 :    1962140 : bool WallClockTimer::expired() const
      63                 :            : {
      64                 :            :   // whether d_limit is in the past
      65         [ +  + ]:    1962140 :   if (!on()) return false;
      66                 :    1948470 :   return d_limit <= clock::now();
      67                 :            : }
      68                 :            : 
      69                 :            : /*---------------------------------------------------------------------------*/
      70                 :            : 
      71                 :       1760 : const char* toString(Resource r)
      72                 :            : {
      73 [ +  - ][ -  - ]:       1760 :   switch (r)
         [ +  + ][ +  + ]
         [ -  - ][ +  - ]
         [ -  + ][ +  - ]
         [ +  + ][ -  - ]
      74                 :            :   {
      75                 :         12 :     case Resource::ArithPivotStep: return "ArithPivotStep";
      76                 :          0 :     case Resource::ArithNlCoveringStep: return "ArithNlCoveringStep";
      77                 :          0 :     case Resource::ArithNlLemmaStep: return "ArithNlLemmaStep";
      78                 :          0 :     case Resource::BitblastStep: return "BitblastStep";
      79                 :        285 :     case Resource::BvSatStep: return "BvSatStep";
      80                 :         13 :     case Resource::CnfStep: return "CnfStep";
      81                 :        285 :     case Resource::DecisionStep: return "DecisionStep";
      82                 :         12 :     case Resource::LemmaStep: return "LemmaStep";
      83                 :          0 :     case Resource::NewSkolemStep: return "NewSkolemStep";
      84                 :          0 :     case Resource::ParseStep: return "ParseStep";
      85                 :        285 :     case Resource::PreprocessStep: return "PreprocessStep";
      86                 :          0 :     case Resource::QuantifierStep: return "QuantifierStep";
      87                 :          0 :     case Resource::RestartStep: return "RestartStep";
      88                 :         13 :     case Resource::RewriteStep: return "RewriteStep";
      89                 :        285 :     case Resource::SatConflictStep: return "SatConflictStep";
      90                 :          0 :     case Resource::SygusCheckStep: return "SygusCheckStep";
      91                 :        285 :     case Resource::TheoryCheckStep: return "TheoryCheckStep";
      92                 :        285 :     case Resource::TheoryFullCheckStep: return "TheoryFullCheckStep";
      93                 :          0 :     case Resource::FindSynthStep: return "FindSynthStep";
      94                 :          0 :     default: return "?Resource?";
      95                 :            :   }
      96                 :            : }
      97                 :       1760 : std::ostream& operator<<(std::ostream& os, Resource r)
      98                 :            : {
      99                 :       1760 :   return os << toString(r);
     100                 :            : }
     101                 :            : 
     102                 :            : struct ResourceManager::Statistics
     103                 :            : {
     104                 :            :   ReferenceStat<uint64_t> d_resourceUnitsUsed;
     105                 :            :   IntStat d_spendResourceCalls;
     106                 :            :   HistogramStat<theory::InferenceId> d_inferenceIdSteps;
     107                 :            :   HistogramStat<Resource> d_resourceSteps;
     108                 :            :   Statistics(StatisticsRegistry& stats);
     109                 :            : };
     110                 :            : 
     111                 :      74946 : ResourceManager::Statistics::Statistics(StatisticsRegistry& stats)
     112                 :            :     : d_resourceUnitsUsed(
     113                 :            :         stats.registerReference<uint64_t>("resource::resourceUnitsUsed")),
     114                 :      74946 :       d_spendResourceCalls(stats.registerInt("resource::spendResourceCalls")),
     115                 :            :       d_inferenceIdSteps(stats.registerHistogram<theory::InferenceId>(
     116                 :      74946 :           "resource::steps::inference-id")),
     117                 :            :       d_resourceSteps(
     118                 :     149892 :           stats.registerHistogram<Resource>("resource::steps::resource"))
     119                 :            : {
     120                 :      74946 : }
     121                 :            : 
     122                 :          0 : bool parseOption(const std::string& optarg, std::string& name, uint64_t& weight)
     123                 :            : {
     124                 :          0 :   auto pos = optarg.find('=');
     125                 :            :   // Check if there is a '='
     126         [ -  - ]:          0 :   if (pos == std::string::npos) return false;
     127                 :            :   // The name is the part before '='
     128                 :          0 :   name = optarg.substr(0, pos);
     129                 :            :   // The weight is the part after '='
     130                 :          0 :   std::string num = optarg.substr(pos + 1);
     131                 :            :   std::size_t converted;
     132                 :          0 :   weight = std::stoull(num, &converted);
     133                 :            :   // Check everything after '=' was converted
     134                 :          0 :   return converted == num.size();
     135                 :            : }
     136                 :            : 
     137                 :            : template <typename T, typename Weights>
     138                 :          0 : bool setWeight(const std::string& name, uint64_t weight, Weights& weights)
     139                 :            : {
     140                 :            :   using theory::toString;
     141         [ -  - ]:          0 :   for (std::size_t i = 0; i < weights.size(); ++i)
     142                 :            :   {
     143         [ -  - ]:          0 :     if (name == toString(static_cast<T>(i)))
     144                 :            :     {
     145                 :          0 :       weights[i] = weight;
     146                 :          0 :       return true;
     147                 :            :     }
     148                 :            :   }
     149                 :          0 :   return false;
     150                 :            : }
     151                 :            : 
     152                 :            : /*---------------------------------------------------------------------------*/
     153                 :            : 
     154                 :      74946 : ResourceManager::ResourceManager(StatisticsRegistry& stats,
     155                 :      74946 :                                  const Options& options)
     156                 :            :     : d_options(options),
     157                 :            :       d_enabled(true),
     158                 :            :       d_perCallTimer(),
     159                 :            :       d_cumulativeTimeUsed(0),
     160                 :            :       d_cumulativeResourceUsed(0),
     161                 :            :       d_thisCallResourceUsed(0),
     162                 :            :       d_thisCallResourceBudget(0),
     163                 :      74946 :       d_statistics(new ResourceManager::Statistics(stats))
     164                 :            : {
     165                 :      74946 :   d_statistics->d_resourceUnitsUsed.set(d_cumulativeResourceUsed);
     166                 :            : 
     167                 :      74946 :   d_infidWeights.fill(1);
     168                 :      74946 :   d_resourceWeights.fill(1);
     169         [ -  + ]:      74946 :   for (const auto& opt : d_options.base.resourceWeightHolder)
     170                 :            :   {
     171                 :          0 :     std::string name;
     172                 :            :     uint64_t weight;
     173         [ -  - ]:          0 :     if (parseOption(opt, name, weight))
     174                 :            :     {
     175         [ -  - ]:          0 :       if (setWeight<theory::InferenceId>(name, weight, d_infidWeights))
     176                 :          0 :         continue;
     177         [ -  - ]:          0 :       if (setWeight<Resource>(name, weight, d_resourceWeights)) continue;
     178                 :          0 :       throw OptionException("Did not recognize resource type " + name);
     179                 :            :     }
     180                 :            :   }
     181                 :      74946 : }
     182                 :            : 
     183                 :      69972 : ResourceManager::~ResourceManager() {}
     184                 :            : 
     185                 :          0 : uint64_t ResourceManager::getResourceUsage() const
     186                 :            : {
     187                 :          0 :   return d_cumulativeResourceUsed;
     188                 :            : }
     189                 :            : 
     190                 :          0 : uint64_t ResourceManager::getTimeUsage() const { return d_cumulativeTimeUsed; }
     191                 :            : 
     192                 :       6930 : uint64_t ResourceManager::getRemainingTime() const
     193                 :            : {
     194                 :       6930 :   return d_options.base.perCallMillisecondLimit - d_perCallTimer.elapsed();
     195                 :            : }
     196                 :            : 
     197                 :          0 : uint64_t ResourceManager::getResourceRemaining() const
     198                 :            : {
     199         [ -  - ]:          0 :   if (d_options.base.cumulativeResourceLimit <= d_cumulativeResourceUsed)
     200                 :          0 :     return 0;
     201                 :          0 :   return d_options.base.cumulativeResourceLimit - d_cumulativeResourceUsed;
     202                 :            : }
     203                 :            : 
     204                 :  134796000 : void ResourceManager::spendResource(uint64_t amount)
     205                 :            : {
     206                 :  134796000 :   ++d_statistics->d_spendResourceCalls;
     207                 :  134796000 :   d_cumulativeResourceUsed += amount;
     208                 :            : 
     209         [ +  - ]:  134796000 :   Trace("limit") << "ResourceManager::spendResource()" << std::endl;
     210                 :  134796000 :   d_thisCallResourceUsed += amount;
     211         [ +  + ]:  134796000 :   if (out())
     212                 :            :   {
     213         [ +  - ]:     692245 :     Trace("limit") << "ResourceManager::spendResource: interrupt!" << std::endl;
     214         [ +  - ]:    1384490 :     Trace("limit") << "          on call "
     215                 :     692245 :                    << d_statistics->d_spendResourceCalls.get() << std::endl;
     216         [ +  + ]:     692245 :     if (outOfTime())
     217                 :            :     {
     218         [ +  - ]:    1383820 :       Trace("limit") << "ResourceManager::spendResource: elapsed time"
     219                 :     691911 :                      << d_perCallTimer.elapsed() << std::endl;
     220                 :            :     }
     221                 :            : 
     222         [ +  + ]:    1384490 :     for (Listener* l : d_listeners)
     223                 :            :     {
     224                 :     692245 :       l->notify();
     225                 :            :     }
     226                 :            :   }
     227                 :  134796000 : }
     228                 :            : 
     229                 :  133384000 : void ResourceManager::spendResource(Resource r)
     230                 :            : {
     231                 :  133384000 :   std::size_t i = static_cast<std::size_t>(r);
     232 [ -  + ][ -  + ]:  133384000 :   Assert(d_resourceWeights.size() > i);
                 [ -  - ]
     233                 :  133384000 :   d_statistics->d_resourceSteps << r;
     234                 :  133384000 :   spendResource(d_resourceWeights[i]);
     235                 :  133384000 : }
     236                 :            : 
     237                 :          0 : uint64_t ResourceManager::getResource(Resource r) const
     238                 :            : {
     239                 :          0 :   return d_statistics->d_resourceSteps.getValue(r);
     240                 :            : }
     241                 :            : 
     242                 :    1411630 : void ResourceManager::spendResource(theory::InferenceId iid)
     243                 :            : {
     244                 :    1411630 :   std::size_t i = static_cast<std::size_t>(iid);
     245 [ -  + ][ -  + ]:    1411630 :   Assert(d_infidWeights.size() > i);
                 [ -  - ]
     246                 :    1411630 :   d_statistics->d_inferenceIdSteps << iid;
     247                 :    1411630 :   spendResource(d_infidWeights[i]);
     248                 :    1411630 : }
     249                 :            : 
     250                 :      59653 : void ResourceManager::beginCall()
     251                 :            : {
     252                 :            :   // refresh here if not already done so
     253                 :      59653 :   refresh();
     254                 :            :   // begin call
     255                 :      59653 :   d_perCallTimer.set(d_options.base.perCallMillisecondLimit);
     256                 :      59653 :   d_thisCallResourceUsed = 0;
     257                 :            : 
     258         [ -  + ]:      59653 :   if (d_options.base.cumulativeResourceLimit > 0)
     259                 :            :   {
     260                 :            :     // Compute remaining cumulative resource budget
     261                 :          0 :     d_thisCallResourceBudget =
     262                 :          0 :         d_options.base.cumulativeResourceLimit - d_cumulativeResourceUsed;
     263                 :            :   }
     264         [ +  + ]:      59653 :   if (d_options.base.perCallResourceLimit > 0)
     265                 :            :   {
     266                 :            :     // Check if per-call resource budget is even smaller
     267         [ -  + ]:          2 :     if (d_options.base.perCallResourceLimit < d_thisCallResourceBudget)
     268                 :            :     {
     269                 :          0 :       d_thisCallResourceBudget = d_options.base.perCallResourceLimit;
     270                 :            :     }
     271                 :            :   }
     272                 :      59653 : }
     273                 :            : 
     274                 :     119225 : void ResourceManager::refresh()
     275                 :            : {
     276                 :     119225 :   d_cumulativeTimeUsed += d_perCallTimer.elapsed();
     277                 :     119225 :   d_perCallTimer.set(0);
     278                 :     119225 :   d_thisCallResourceUsed = 0;
     279                 :     119225 : }
     280                 :            : 
     281                 :         10 : bool ResourceManager::limitOn() const
     282                 :            : {
     283                 :         10 :   return (d_options.base.cumulativeResourceLimit > 0)
     284         [ +  - ]:         10 :          || (d_options.base.perCallMillisecondLimit > 0)
     285 [ +  - ][ -  + ]:         20 :          || (d_options.base.perCallResourceLimit > 0);
     286                 :            : }
     287                 :            : 
     288                 :  186676000 : bool ResourceManager::outOfResources() const
     289                 :            : {
     290         [ +  + ]:  186676000 :   if (!d_enabled)
     291                 :            :   {
     292                 :     986093 :     return false;
     293                 :            :   }
     294         [ +  + ]:  185690000 :   if (d_options.base.perCallResourceLimit > 0)
     295                 :            :   {
     296                 :            :     // Check if per-call resources are exhausted
     297         [ +  + ]:        538 :     if (d_thisCallResourceUsed >= d_options.base.perCallResourceLimit)
     298                 :            :     {
     299                 :        338 :       return true;
     300                 :            :     }
     301                 :            :   }
     302         [ -  + ]:  185690000 :   if (d_options.base.cumulativeResourceLimit > 0)
     303                 :            :   {
     304                 :            :     // Check if cumulative resources are exhausted
     305         [ -  - ]:          0 :     if (d_cumulativeResourceUsed >= d_options.base.cumulativeResourceLimit)
     306                 :            :     {
     307                 :          0 :       return true;
     308                 :            :     }
     309                 :            :   }
     310                 :  185690000 :   return false;
     311                 :            : }
     312                 :            : 
     313                 :  187370000 : bool ResourceManager::outOfTime() const
     314                 :            : {
     315         [ +  + ]:  187370000 :   if (!d_enabled)
     316                 :            :   {
     317                 :     986093 :     return false;
     318                 :            :   }
     319         [ +  + ]:  186384000 :   if (d_options.base.perCallMillisecondLimit == 0) return false;
     320                 :    1962140 :   return d_perCallTimer.expired();
     321                 :            : }
     322                 :            : 
     323                 :      74926 : void ResourceManager::registerListener(Listener* listener)
     324                 :            : {
     325                 :      74926 :   return d_listeners.push_back(listener);
     326                 :            : }
     327                 :            : 
     328                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14