dlvhex  2.5.0
src/Benchmarking.cpp
Go to the documentation of this file.
00001 /* dlvhex -- Answer-Set Programming with external interfaces.
00002  * Copyright (C) 2005-2007 Roman Schindlauer
00003  * Copyright (C) 2006-2015 Thomas Krennwallner
00004  * Copyright (C) 2009-2016 Peter Schüller
00005  * Copyright (C) 2011-2016 Christoph Redl
00006  * Copyright (C) 2015-2016 Tobias Kaminski
00007  * Copyright (C) 2015-2016 Antonius Weinzierl
00008  *
00009  * This file is part of dlvhex.
00010  *
00011  * dlvhex is free software; you can redistribute it and/or modify it
00012  * under the terms of the GNU Lesser General Public License as
00013  * published by the Free Software Foundation; either version 2.1 of
00014  * the License, or (at your option) any later version.
00015  *
00016  * dlvhex is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with dlvhex; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00024  * 02110-1301 USA.
00025  */
00026 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif                           // HAVE_CONFIG_H
00039 
00040 #include "dlvhex2/Benchmarking.h"
00041 #include <boost/foreach.hpp>
00042 #include <iostream>
00043 #include <set>
00044 #include <boost/thread/mutex.hpp>
00045 
00046 DLVHEX_NAMESPACE_BEGIN
00047 
00048 namespace benchmark
00049 {
00050 
00051     namespace simple
00052     {
00053 
00054         BenchmarkController::Stat::Stat(const std::string& name):
00055         name(name), count(0), prints(0), start(), duration(), running(false) {
00056         }
00057 
00058         // init, display start of benchmarking
00059         BenchmarkController::BenchmarkController():
00060         myID(0), maxID(0), instrumentations(), name2id(), output(&(std::cerr)), printSkip(0), sus(0) {
00061             myID = getInstrumentationID("BenchmarkController lifetime");
00062             start(myID);
00063         }
00064 
00065         // destruct, output benchmark results
00066         BenchmarkController::~BenchmarkController() {
00067             // stop only my ID
00068             // (do not call stop() as the mutex might hang)
00069             // (this code must succeed at any time!)
00070             Stat& st = instrumentations[myID];
00071             // ignore level
00072             if( st.running ) {
00073                 st.duration += boost::posix_time::microsec_clock::local_time() - st.start;
00074                 st.running = false;
00075                 st.count ++;
00076             }
00077 
00078             BOOST_FOREACH(const Stat& st, instrumentations) {
00079                 printInformation(st);
00080             }
00081         }
00082 
00083         namespace
00084         {
00085             BenchmarkController* instance = 0;
00086         }
00087 
00088         void BenchmarkController::finish() {
00089             if( instance )
00090                 delete instance;
00091             instance = 0;
00092         }
00093 
00094         BenchmarkController& BenchmarkController::Instance() {
00095             if( instance == 0 )
00096                 instance = new BenchmarkController;
00097             return *instance;
00098         }
00099 
00100         // output stream
00101         void BenchmarkController::setOutput(std::ostream* o) {
00102             output = o;
00103         }
00104 
00105         // amount of accumulated output (default: each call)
00106         void BenchmarkController::setPrintInterval(Count skip) {
00107             printSkip = skip;
00108         }
00109 
00110         // get ID or register new one
00111         ID BenchmarkController::getInstrumentationID(const std::string& name) {
00112             boost::mutex::scoped_lock lock(mutex);
00113             std::map<std::string, ID>::const_iterator it = name2id.find(name);
00114             if( it == name2id.end() ) {
00115                 ID newid = maxID;
00116                 instrumentations.push_back(Stat(name));
00117                 name2id[name] = newid;
00118                 maxID++;
00119                 return newid;
00120             }
00121             else {
00122                 return it->second;
00123             }
00124         }
00125 
00126         void BenchmarkController::suspend() {
00127             if( sus == 0 ) {
00128                 myID = getInstrumentationID("BMController suspend");
00129                 start(myID);
00130             }
00131             boost::mutex::scoped_lock lock(mutex);
00132             sus++;
00133         }
00134 
00135         void BenchmarkController::resume() {
00136             boost::mutex::scoped_lock lock(mutex);
00137             sus--;
00138             if( sus == 0 ) {
00139                 myID = getInstrumentationID("BMController suspend");
00140                 stop(myID);
00141             }
00142         }
00143 
00144         std::string BenchmarkController::count(const std::string& name, int width) const
00145         {
00146             if (sus > 0) return "-";
00147             std::map<std::string, ID>::const_iterator it = name2id.find(name);
00148             if( it == name2id.end() )
00149                 return "-";
00150             benchmark::ID id = it->second;
00151             std::ostringstream oss;
00152             oss << std::setw(width) << getStat(id).count;
00153             return oss.str();
00154         }
00155 
00156         std::string BenchmarkController::duration(const std::string& name, int width) const
00157         {
00158             if (sus > 0) return "-";
00159             std::map<std::string, ID>::const_iterator it = name2id.find(name);
00160             if( it == name2id.end() )
00161                 return "-";
00162             benchmark::ID id = it->second;
00163             std::ostringstream oss;
00164             printInSecs(oss, getStat(id).duration, width);
00165             return oss.str();
00166         }
00167 
00168         // stop and do not record, handle non-started id's gracefully
00169         void BenchmarkController::invalidate(ID id) {
00170             if (sus > 0) return;
00171             boost::mutex::scoped_lock lock(mutex);
00172             Stat& st = instrumentations[id];
00173 
00174             if( st.running )
00175                 st.running = false;
00176         }
00177 
00178         // copy data from one id to another id and call stop() on that other id
00179         // e.g. do this for several interesting benchmarks at first model
00180         void BenchmarkController::snapshot(ID id, ID intoID) {
00181             if (sus > 0) return;
00182             {
00183                 boost::mutex::scoped_lock lock(mutex);
00184                 Stat& st = instrumentations[id];
00185                 Stat& intost = instrumentations[intoID];
00186 
00187                 // copy (overwrite old snapshot!)
00188                 // do not copy name!
00189                 intost.count = st.count;
00190                 // do not copy prints! (might produce duplicates)
00191                 intost.start = st.start;
00192                 intost.duration = st.duration;
00193                 intost.running = st.running;
00194             }
00195             // stop but do not count
00196             stop(intoID, false);
00197         }
00198 
00199         void BenchmarkController::snapshot(const std::string& fromstr, const std::string& tostr) {
00200             if (sus > 0) return;
00201             ID idfrom = getInstrumentationID(fromstr);
00202             ID idto = getInstrumentationID(tostr);
00203             snapshot(idfrom, idto);
00204         }
00205 
00206     }                            // namespace simple
00207 
00208     namespace nestingAware
00209     {
00210 
00211         NestingAwareController::Stat::Stat(const std::string& name, Duration printInterval):
00212         name(name), count(0), duration(), pureDuration(), level(0),
00213         nextPrint(boost::posix_time::microsec_clock::local_time() + printInterval) {
00214         }
00215 
00216         NestingAwareController::Current::Current(ID which):
00217         which(which), firststart(), start() {}
00218 
00219         // init, display start of benchmarking
00220         NestingAwareController::NestingAwareController():
00221         myID(0), maxID(0), instrumentations(), name2id(), current(), output(&(std::cerr)),
00222                                  // print continuously all 10 seconds
00223         printInterval(boost::posix_time::seconds(10.0)) {
00224             myID = getInstrumentationID("BenchmarkController lifetime");
00225             start(myID);
00226         }
00227 
00228         // destruct, output benchmark results
00229         NestingAwareController::~NestingAwareController() {
00230             if( !output )
00231                 return;
00232 
00233             // stop only my ID
00234             // (do not call stop() as the mutex might hang)
00235             // (this code must succeed at any time!)
00236             Stat& st = instrumentations[myID];
00237             Time now = boost::posix_time::microsec_clock::local_time();
00238             if( !current.empty() && current.back().which == myID ) {
00239                 // this is a very clean exit indeed!
00240                 // (we are running)
00241                 st.pureDuration += now - current.back().start;
00242                 st.duration += now - current.back().firststart;
00243                 st.count ++;
00244                 current.pop_back();
00245             }
00246             else {
00247                 // not sure what happened, we are somewhere
00248                 // -> take the furst current record
00249                 //    (this is the usual use case for myID)
00250                                  // to be absolutely sure we will not segfault/throw here
00251                 if( !current.empty() ) {
00252                     st.duration = now - current[0].firststart;
00253                                  // TODO maybe this is bad
00254                     st.pureDuration = now - current[0].start;
00255                     st.count ++;
00256                 }
00257             }
00258 
00259             // sort by pure duration, descending
00260             class PureSortPredicate
00261             {
00262                 public:
00263                     static bool isHigher(const Stat& s1, const Stat& s2) {
00264                         return s1.pureDuration > s2.pureDuration;
00265                     }
00266             };
00267             std::sort(instrumentations.begin(), instrumentations.end(), PureSortPredicate::isHigher);
00268 
00269             // compute total pure duration
00270             // (this is necessary to compute relative durations; relative durations are useful
00271             //  in ASP applications with user interaction where no two runs are the same)
00272             Duration total;
00273             BOOST_FOREACH(const Stat& st, instrumentations) {
00274                                  // this way we do not count snapshot "... to first model" pure durations
00275                 if( st.count != 0 )
00276                     total += st.pureDuration;
00277             }
00278 
00279             // print sorted summary
00280             BOOST_FOREACH(const Stat& st, instrumentations) {
00281                 (*output) <<
00282                     "BM:" <<     // std::setw(2) << int(&st-instrumentations.data()) << " " <<
00283                     std::setw(30) << st.name <<
00284                     ": count:" << std::setw(8) << st.count;
00285                 (*output) << " total:";
00286                 printInSecs(*output, st.duration, 4) << "s pure:";
00287                 printInSecs(*output, st.pureDuration, 4) << "s";
00288                 if( st.count > 0 ) {
00289                     float ratio = (1000L * st.pureDuration.total_milliseconds() / total.total_milliseconds())/1000.0;
00290                     (*output) << " (" << std::setw(4) << std::setprecision(1) << std::fixed << (100.0*ratio) << "%)" <<
00291                         " avg:";
00292                     printInSecs(*output, st.duration/st.count, 4) << "s";
00293                 }
00294                 (*output) << std::endl;
00295             }
00296 
00297             #ifndef NDEBUG
00298             // to verify if all partial times sum up to the total runtime
00299             if( output ) {
00300                 (*output) << "Sum of pure durations = ";
00301                 printInSecs(std::cerr, total) << "s." << std::endl;
00302             }
00303             #endif
00304         }
00305 
00306         namespace
00307         {
00308             NestingAwareController* instance = 0;
00309         }
00310 
00311         void NestingAwareController::finish() {
00312             if( instance )
00313                 delete instance;
00314             instance = 0;
00315         }
00316 
00317         NestingAwareController& NestingAwareController::Instance() {
00318             if( instance == 0 )
00319                 instance = new NestingAwareController;
00320             return *instance;
00321         }
00322 
00323         // output stream
00324         void NestingAwareController::setOutput(std::ostream* o) {
00325             output = o;
00326         }
00327 
00328         // amount of accumulated output (default: each call)
00329         void NestingAwareController::setPrintInterval(Count skip) {
00330             // TODO
00331             std::cerr << "not implemented NestingAwareController::setPrintInterval" << std::endl;
00332         }
00333 
00334         // get ID or register new one
00335         ID NestingAwareController::getInstrumentationID(const std::string& name) {
00336             boost::mutex::scoped_lock lock(mutex);
00337             std::map<std::string, ID>::const_iterator it = name2id.find(name);
00338             if( it == name2id.end() ) {
00339                 ID newid = maxID;
00340                 instrumentations.push_back(Stat(name, printInterval));
00341                 name2id[name] = newid;
00342                 maxID++;
00343                 return newid;
00344             }
00345             else {
00346                 return it->second;
00347             }
00348         }
00349 
00350         void NestingAwareController::suspend() {
00351             if( sus == 0 ) {
00352                 myID = getInstrumentationID("BMController suspend");
00353                 start(myID);
00354             }
00355             boost::mutex::scoped_lock lock(mutex);
00356             sus++;
00357         }
00358 
00359         void NestingAwareController::resume() {
00360             boost::mutex::scoped_lock lock(mutex);
00361             sus--;
00362             if( sus == 0 ) {
00363                 myID = getInstrumentationID("BMController suspend");
00364                 stop(myID);
00365             }
00366         }
00367 
00368         std::string NestingAwareController::count(const std::string& name, int width) const
00369         {
00370             std::map<std::string, ID>::const_iterator it = name2id.find(name);
00371             if( it == name2id.end() )
00372                 return "-";
00373             benchmark::ID id = it->second;
00374             std::ostringstream oss;
00375             oss << std::setw(width) << getStat(id).count;
00376             return oss.str();
00377         }
00378 
00379         std::string NestingAwareController::duration(const std::string& name, int width) const
00380         {
00381             std::map<std::string, ID>::const_iterator it = name2id.find(name);
00382             if( it == name2id.end() )
00383                 return "-";
00384             benchmark::ID id = it->second;
00385             std::ostringstream oss;
00386             // we return pure duration as it is the purpose of NestingAwareController
00387             printInSecs(oss, getStat(id).pureDuration, width);
00388             return oss.str();
00389         }
00390 
00391         // stop and do not record, handle non-started id's gracefully
00392         void NestingAwareController::invalidate(ID id) {
00393             if( !current.empty() && current.back().which == id ) {
00394                 // save start time of pure period (we do not want to lose this)
00395                 Time start = current.back().start;
00396                 // destroy current without counting it
00397                 current.pop_back();
00398                 // set start time to new top-of-stack
00399                 if( current.size() > 1 ) {
00400                     current[current.size()-2].start = start;
00401                 }
00402             }
00403         }
00404 
00405         // copy data from one id to another id and call stop() on that other id
00406         // e.g. do this for several interesting benchmarks at first model
00407         void NestingAwareController::snapshot(ID id, ID intoID) {
00408             boost::mutex::scoped_lock lock(mutex);
00409             Stat& st = instrumentations[id];
00410             Stat& intost = instrumentations[intoID];
00411 
00412             // copy (overwrites old snapshot!)
00413 
00414             Time now = boost::posix_time::microsec_clock::local_time();
00415             intost.count = st.count;
00416             intost.duration = st.duration;
00417             intost.pureDuration = st.pureDuration;
00418             if( current.back().which == id ) {
00419                 // if top level entry in current has which=id then add to pureDuration
00420                 intost.pureDuration += now - current.back().start;
00421             }
00422             // find bottom-most level entry in current where which=id and add to duration (if exists)
00423             for(unsigned u = 0; u < current.size(); ++u) {
00424                 if( current[u].which == id ) {
00425                     intost.duration += now - current[u].firststart;
00426                     break;
00427                 }
00428             }
00429         }
00430 
00431         // print information about stat
00432         void NestingAwareController::printInformation(const Stat& st) {
00433             if( output ) {
00434                 (*output) <<
00435                     "BM:" <<     // std::setw(2) << int(&st-instrumentations.data()) << " " <<
00436                     std::setw(30) << st.name <<
00437                     ": count:" << std::setw(8) << st.count;
00438                 (*output) << " total:";
00439                 printInSecs(*output, st.duration, 4) << "s pure:";
00440                 printInSecs(*output, st.pureDuration, 4) << "s avg:";
00441                 if( st.count > 0 ) {
00442                     printInSecs(*output, st.duration/st.count, 4) << "s";
00443                 }
00444                 else {
00445                     (*output) << "  -.---s";
00446                 }
00447                 (*output) << std::endl;
00448             }
00449         }
00450 
00451         void NestingAwareController::snapshot(const std::string& fromstr, const std::string& tostr) {
00452             ID idfrom = getInstrumentationID(fromstr);
00453             ID idto = getInstrumentationID(tostr);
00454             snapshot(idfrom, idto);
00455         }
00456 
00457     }                            // namespace simple
00458 
00459 }                                // namespace benchmark
00460 
00461 
00462 DLVHEX_NAMESPACE_END
00463 
00464 
00465 // vim:expandtab:ts=4:sw=4:
00466 // mode: C++
00467 // End: