dlvhex
2.5.0
|
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: