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 00035 #ifndef DLVHEX_H_BENCHMARKING_INCLUDED_1555 00036 #define DLVHEX_H_BENCHMARKING_INCLUDED_1555 00037 00038 #include "dlvhex2/PlatformDefinitions.h" 00039 00040 #include <boost/scope_exit.hpp> 00041 // seems to be required for scope_exit 00042 #include <boost/typeof/typeof.hpp> 00043 #include <boost/date_time/posix_time/posix_time.hpp> 00044 #include <boost/thread/mutex.hpp> 00045 #include <vector> 00046 #include <ostream> 00047 00048 // Benchmarking is always compiled into dlvhex, 00049 // but benchmarking of dlvhex itself is only 00050 // activated if you configure with --enable-debug 00051 // 00052 // Plugins can use benchmarking of dlvhex by doing 00053 // #define DLVHEX_BENCHMARK 00054 // #include <dlvhex2/Benchmarking.h> 00055 // in a .cpp file that wants to use benchmarking. 00056 // 00057 // usage example: 00058 // 00059 // DLVHEX_BENCHMARK_REGISTER(sid1,"calling dlv"); 00060 // DLVHEX_BENCHMARK_REGISTER(sid2,"fork+exec dlv"); 00061 // DLVHEX_BENCHMARK_REGISTER(sid3,"parse dlv result"); 00062 // 00063 // DLVHEX_BENCHMARK_START(sid1) 00064 // DLVHEX_BENCHMARK_START(sid2) 00065 // // fork and exec 00066 // DLVHEX_BENCHMARK_STOP(sid2) 00067 // 00068 // { 00069 // DLVHEX_BENCHMARK_SCOPE(sid3) 00070 // parse result 00071 // ... 00072 // } 00073 // DLVHEX_BENCHMARK_STOP(sid1) 00074 // DLVHEX_BENCHMARK_COUNT(sid4,someinteger) 00075 // DLVHEX_BENCHMARK_REGISTER_AND_START(sid6,"reg start") 00076 // { 00077 // DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid5,"reg scope") 00078 // ... 00079 // } 00080 // DLVHEX_BENCHMARK_STOP(sid6) 00081 // 00082 // if you want to have a benchmark scope within a template, you need to use 00083 // DLVHEX_BENCHMARK_REGISTER_AND_SCOPE_TPL(sid7,"reg scope in template") 00084 // 00085 // you can also manage the stat IDs yourself 00086 // (e.g., for creating one instrumentation per custom external atom, 00087 // not only one for some base class) 00088 // #if defined(DLVHEX_BENCHMARK) 00089 // dlvhex::benchmark::ID myStoredSid = 00090 // dlvhex::benchmark::BenchmarkController::Instance().getInstrumentationID("my message"); 00091 // #endif 00092 // 00093 // invalidate(sid) and INVALIDATE is used to abort instrumentations that were started but should not be counted 00094 // (e.g., if there is no model, we will not have a time to first model) 00095 // invalidating a non-running counter does nothing 00096 // 00097 // there are two benchmark controllers that can be used: 00098 // * simple: (#define DLVHEX_BENCHMARK_SIMPLE) 00099 // + fast and simple 00100 // - counts overlapping instrumentalizations twice 00101 // (not if they have the same name) 00102 // - gives less intuitive timing results 00103 // * nesting-aware: (#define DLVHEX_BENCHMARK_NESTINGAWARE) 00104 // - a bit more complex 00105 // + counts "pure" time where an instrumentalization 00106 // was the last activated one (uses a stack of instrumentalizations) 00107 // + gives more intuitive timing results 00108 00109 //#define DLVHEX_BENCHMARK_SIMPLE 00110 #define DLVHEX_BENCHMARK_NESTINGAWARE 00111 00112 #if defined(DLVHEX_BENCHMARK) 00113 # define DLVHEX_BENCHMARK_REGISTER(sid,msg) \ 00114 static DLVHEX_NAMESPACE benchmark::ID sid = DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().getInstrumentationID(msg) 00115 # define DLVHEX_BENCHMARK_START(sid) \ 00116 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().start(sid) 00117 # define DLVHEX_BENCHMARK_STOP(sid) \ 00118 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().stop(sid) 00119 # define DLVHEX_BENCHMARK_INVALIDATE(sid) \ 00120 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().invalidate(sid) 00121 # define DLVHEX_BENCHMARK_SUSPEND(sid) \ 00122 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().stop(sid,false) 00123 # define DLVHEX_BENCHMARK_COUNT(sid,num) \ 00124 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().count(sid,num) 00125 # define DLVHEX_BENCHMARK_SCOPE(sid) \ 00126 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().start(sid); \ 00127 BOOST_SCOPE_EXIT( (sid) ) \ 00128 { \ 00129 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().stop(sid); \ 00130 } BOOST_SCOPE_EXIT_END 00131 # define DLVHEX_BENCHMARK_SUSPEND_SCOPE(sid) \ 00132 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().stop(sid,false); \ 00133 BOOST_SCOPE_EXIT( (sid) ) \ 00134 { \ 00135 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().start(sid); \ 00136 } BOOST_SCOPE_EXIT_END 00137 # define DLVHEX_BENCHMARK_SCOPE_TPL(sid) \ 00138 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().start(sid); \ 00139 BOOST_SCOPE_EXIT_TPL( (sid) ) \ 00140 { \ 00141 DLVHEX_NAMESPACE benchmark::BenchmarkController::Instance().stop(sid); \ 00142 } BOOST_SCOPE_EXIT_END 00143 # define DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,msg) \ 00144 DLVHEX_BENCHMARK_REGISTER(sid,msg); DLVHEX_BENCHMARK_SCOPE(sid); 00145 # define DLVHEX_BENCHMARK_REGISTER_AND_SCOPE_TPL(sid,msg) \ 00146 DLVHEX_BENCHMARK_REGISTER(sid,msg); DLVHEX_BENCHMARK_SCOPE_TPL(sid); 00147 # define DLVHEX_BENCHMARK_REGISTER_AND_START(sid,msg) \ 00148 DLVHEX_BENCHMARK_REGISTER(sid,msg); DLVHEX_BENCHMARK_START(sid); 00149 # define DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sid,msg,num) \ 00150 DLVHEX_BENCHMARK_REGISTER(sid,msg); DLVHEX_BENCHMARK_COUNT(sid,num); 00151 #else 00152 # define DLVHEX_BENCHMARK_REGISTER(sid,msg) do { } while(0) 00153 # define DLVHEX_BENCHMARK_START(sid) do { } while(0) 00154 # define DLVHEX_BENCHMARK_STOP(sid) do { } while(0) 00155 # define DLVHEX_BENCHMARK_INVALIDATE(sid) do { } while(0) 00156 # define DLVHEX_BENCHMARK_SUSPEND(sid) do { } while(0) 00157 # define DLVHEX_BENCHMARK_SUSPEND_SCOPE(sid) do { } while(0) 00158 # define DLVHEX_BENCHMARK_COUNT(sid,num) do { } while(0) 00159 # define DLVHEX_BENCHMARK_SCOPE(sid) do { } while(0) 00160 # define DLVHEX_BENCHMARK_SCOPE_TPL(sid) do { } while(0) 00161 # define DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,msg) do { } while(0) 00162 # define DLVHEX_BENCHMARK_REGISTER_AND_SCOPE_TPL(sid,msg) do { } while(0) 00163 # define DLVHEX_BENCHMARK_REGISTER_AND_START(sid,msg) do { } while(0) 00164 # define DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sid,msg,num) do { } while(0) 00165 #endif // defined(DLVHEX_BENCHMARK) 00166 00167 DLVHEX_NAMESPACE_BEGIN 00168 00169 namespace benchmark 00170 { 00171 00172 typedef unsigned ID; 00173 typedef unsigned Count; 00174 typedef boost::posix_time::ptime Time; 00175 typedef boost::posix_time::time_duration Duration; 00176 00177 namespace simple 00178 { 00179 00181 class DLVHEX_EXPORT BenchmarkController 00182 { 00183 public: 00185 struct Stat 00186 { 00188 std::string name; 00190 Count count; 00192 Count level; 00194 Count prints; 00196 Time start; 00198 Duration duration; 00200 bool running; 00201 00204 Stat(const std::string& name); 00205 }; 00206 00207 public: 00212 inline std::ostream& printInSecs(std::ostream& o, const Duration& d, int width=0) const; 00213 00214 // comfort formatting 00218 std::string count(const std::string& identifier, int width=0) const; 00222 std::string duration(const std::string& identifier, int width=0) const; 00223 00226 // inline for performance 00227 inline void printInformation(const Stat& st); 00230 inline void printInformationContinous( 00231 // inline for performance 00232 Stat& st, const Duration& dur); 00233 00234 public: 00235 // 00238 static BenchmarkController& Instance(); 00239 00243 static void finish(); 00244 00248 ~BenchmarkController(); 00249 00250 // 00251 // configure 00252 // 00253 00256 void setOutput(std::ostream* o); 00259 void setPrintInterval(Count skip); 00260 00261 // 00262 // instrumentation points 00263 // 00264 00268 ID getInstrumentationID(const std::string& name); 00273 // inline for performance 00274 inline std::ostream& printCount(std::ostream& out, ID id); 00278 // inline for performance 00279 inline std::ostream& printDuration(std::ostream& out, ID id); 00283 inline const Stat& getStat(ID id) const { return instrumentations[id]; } 00284 00285 // 00286 // record measured things 00287 // 00288 00289 // stop and resume benchmarking 00291 void suspend(); 00293 void resume(); 00294 00295 // start timer 00298 // inline for performance 00299 inline void start(ID id); 00305 // inline for performance 00306 inline void stop(ID id, bool count=true); 00312 // inline for performance 00313 inline void count(ID id, Count increment=1); 00318 void invalidate(ID id); 00319 00325 void snapshot(ID id, ID intoID); 00326 00332 void snapshot(const std::string& fromstr, const std::string& tostr); 00333 00334 private: 00338 BenchmarkController(); 00339 00341 ID myID; 00343 ID maxID; 00345 std::vector<Stat> instrumentations; 00347 std::map<std::string, ID> name2id; 00348 00350 std::ostream* output; 00352 Count printSkip; 00353 00355 boost::mutex mutex; 00357 unsigned sus; 00358 }; 00359 00360 //inline 00361 std::ostream& BenchmarkController::printInSecs(std::ostream& out, const Duration& td, int width) const 00362 { 00363 long in_secs = (long)td.total_milliseconds(); 00364 00365 long secs = in_secs / 1000; 00366 long rest = in_secs % 1000; 00367 00368 out << std::setw(width) << secs << "."; 00369 00370 if (rest < 10) { 00371 out << "00"; 00372 } 00373 else if (rest < 100) { 00374 out << "0"; 00375 } 00376 00377 return out << rest; 00378 } 00379 00380 // print information about stat 00381 // inline for performance 00382 void BenchmarkController::printInformation(const Stat& st) { 00383 if( output ) { 00384 (*output) << 00385 "BM:" << std::setw(30) << st.name << 00386 ": count:" << std::setw(8) << st.count; 00387 (*output) << 00388 " total:"; 00389 printInSecs(*output, st.duration, 4) << 00390 "s avg:"; 00391 if( st.count > 0 ) { 00392 printInSecs(*output, st.duration/st.count, 4); 00393 } 00394 else { 00395 (*output) << " -.---"; 00396 } 00397 (*output) << std::endl; 00398 } 00399 } 00400 00401 // print only count of ID 00402 std::ostream& BenchmarkController::printCount(std::ostream& out, ID id) { 00403 boost::mutex::scoped_lock lock(mutex); 00404 Stat& st = instrumentations[id]; 00405 return out << st.count; 00406 } 00407 00408 std::ostream& BenchmarkController::printDuration(std::ostream& out, ID id) { 00409 boost::mutex::scoped_lock lock(mutex); 00410 Stat& st = instrumentations[id]; 00411 return printInSecs(out, st.duration); 00412 } 00413 00414 // print continuous information about stat 00415 // inline for performance 00416 void BenchmarkController::printInformationContinous(Stat& st, const Duration& dur) { 00417 if( st.prints >= printSkip ) { 00418 st.prints = 0; 00419 if( output ) { 00420 (*output) << 00421 "BM:" << std::setw(30) << st.name << 00422 ": count:" << std::setw(8) << st.count << 00423 " total:"; 00424 printInSecs(*output, st.duration, 4) << "s" << 00425 " last:"; 00426 printInSecs(*output, dur, 2) << "s" << ((st.running)?"(runs)":"") << std::endl; 00427 } 00428 } 00429 else { 00430 st.prints++; 00431 } 00432 } 00433 00434 // inline for performance 00435 // start timer 00436 void BenchmarkController::start(ID id) { 00437 if (sus) return; 00438 boost::mutex::scoped_lock lock(mutex); 00439 Stat& st = instrumentations[id]; 00440 if( !st.running ) { 00441 st.start = boost::posix_time::microsec_clock::local_time(); 00442 st.running = true; 00443 // running once -> level 1 00444 st.level = 1; 00445 } 00446 else { 00447 // increase nesting level 00448 st.level++; 00449 } 00450 } 00451 00452 // inline for performance 00453 // stop and record elapsed time, print stats 00454 void BenchmarkController::stop(ID id, bool count) { 00455 if (sus) return; 00456 boost::mutex::scoped_lock lock(mutex); 00457 Stat& st = instrumentations[id]; 00458 00459 if( st.running && st.level == 1 ) { 00460 Duration dur = boost::posix_time::microsec_clock::local_time() - st.start; 00461 st.duration += dur; 00462 st.running = false; 00463 if( count ) { 00464 st.count++; 00465 printInformationContinous(st,dur); 00466 } 00467 } 00468 else if( st.running ) { 00469 // decrease nesting level 00470 st.level--; 00471 } 00472 } 00473 00474 // record count (no time), print stats 00475 // inline for performance 00476 void BenchmarkController::count(ID id, Count increment) { 00477 if (sus) return; 00478 boost::mutex::scoped_lock lock(mutex); 00479 Stat& s = instrumentations[id]; 00480 s.count += increment; 00481 // only count how often we count, otherwise we might spam 00482 s.prints ++; 00483 printInformationContinous(s,Duration()); 00484 } 00485 00486 } // namespace simple 00487 00488 namespace nestingAware 00489 { 00490 00492 class DLVHEX_EXPORT NestingAwareController 00493 { 00494 public: 00496 struct Stat 00497 { 00499 std::string name; 00501 Count count; 00503 Duration duration; 00505 Duration pureDuration; 00507 Count level; 00509 Time nextPrint; 00510 00513 Stat(const std::string& name, Duration printInterval); 00514 }; 00515 struct Current 00516 { 00518 ID which; 00520 Time firststart; 00522 Time start; 00523 00524 Current(ID which); 00525 }; 00526 00527 public: 00532 inline std::ostream& printInSecs(std::ostream& o, const Duration& d, int width=0) const; 00533 00534 // comfort formatting 00538 std::string count(const std::string& identifier, int width=0) const; 00542 std::string duration(const std::string& identifier, int width=0) const; 00543 00546 void printInformation(const Stat& st); 00549 inline void printInformationContinous(Stat& st, const Duration& dur); 00550 00551 public: 00552 // 00555 static NestingAwareController& Instance(); 00556 00560 static void finish(); 00561 00565 ~NestingAwareController(); 00566 00567 // 00568 // configure 00569 // 00570 00573 void setOutput(std::ostream* o); 00576 void setPrintInterval(Count skip); 00577 00578 // 00579 // instrumentation points 00580 // 00581 00585 ID getInstrumentationID(const std::string& name); 00590 // inline for performance 00591 inline std::ostream& printCount(std::ostream& out, ID id); 00595 // inline for performance 00596 inline std::ostream& printDuration(std::ostream& out, ID id); 00600 inline const Stat& getStat(ID id) const { return instrumentations[id]; } 00601 00602 // 00603 // record measured things 00604 // 00605 00606 // stop and resume benchmarking 00608 void suspend(); 00610 void resume(); 00611 00612 // start timer 00615 // inline for performance 00616 inline void start(ID id); 00622 // inline for performance 00623 inline void stop(ID id, bool count=true); 00629 // inline for performance 00630 inline void count(ID id, Count increment=1); 00635 void invalidate(ID id); 00636 00642 void snapshot(ID id, ID intoID); 00643 00649 void snapshot(const std::string& fromstr, const std::string& tostr); 00650 00651 private: 00655 NestingAwareController(); 00656 00658 ID myID; 00660 ID maxID; 00662 std::vector<Stat> instrumentations; 00664 std::map<std::string, ID> name2id; 00666 std::vector<Current> current; 00667 00669 Duration printInterval; 00671 std::ostream* output; 00672 00674 boost::mutex mutex; 00676 unsigned sus; 00677 }; 00678 00679 //inline 00680 std::ostream& NestingAwareController::printInSecs(std::ostream& out, const Duration& td, int width) const 00681 { 00682 long in_secs = (long)td.total_milliseconds(); 00683 00684 long secs = in_secs / 1000; 00685 long rest = in_secs % 1000; 00686 00687 out << std::setw(width) << secs << "."; 00688 00689 if (rest < 10) { 00690 out << "00"; 00691 } 00692 else if (rest < 100) { 00693 out << "0"; 00694 } 00695 00696 return out << rest; 00697 } 00698 00699 // print only count of ID 00700 std::ostream& NestingAwareController::printCount(std::ostream& out, ID id) { 00701 boost::mutex::scoped_lock lock(mutex); 00702 Stat& st = instrumentations[id]; 00703 return out << st.count; 00704 } 00705 00706 std::ostream& NestingAwareController::printDuration(std::ostream& out, ID id) { 00707 boost::mutex::scoped_lock lock(mutex); 00708 Stat& st = instrumentations[id]; 00709 return printInSecs(out, st.duration); 00710 } 00711 00712 // print continuous information about stat 00713 // inline for performance 00714 void NestingAwareController::printInformationContinous(Stat& st, const Duration& dur) { 00715 if( !output ) 00716 return; 00717 Time now = boost::posix_time::microsec_clock::local_time(); 00718 if( now > st.nextPrint ) { 00719 st.nextPrint = now + printInterval; 00720 (*output) << 00721 "BM:" << std::setw(30) << st.name << 00722 ": count:" << std::setw(8) << st.count << 00723 " total:"; 00724 printInSecs(*output, st.duration, 4) << "s" << 00725 " pure:"; 00726 printInSecs(*output, st.pureDuration, 4) << "s" << 00727 " last:"; 00728 printInSecs(*output, dur, 2) << "s" << std::endl; 00729 } 00730 } 00731 00732 // inline for performance 00733 // start timer 00734 void NestingAwareController::start(ID id) { 00735 boost::mutex::scoped_lock lock(mutex); 00736 Stat& st = instrumentations[id]; 00737 00738 Time now = boost::posix_time::microsec_clock::local_time(); 00739 00740 st.level++; // not running = 0, 1 = not nested 00741 if( !current.empty() ) { 00742 // suspend previously active instrumentation (and record pure time elapsed) 00743 Current& prevc = current.back(); 00744 Stat& prevst = instrumentations[prevc.which]; 00745 prevst.pureDuration += now - prevc.start; 00746 } 00747 00748 // start new instrumentation 00749 current.push_back(Current(id)); 00750 Current& c = current.back(); 00751 c.firststart = now; 00752 c.start = now; 00753 } 00754 00755 // inline for performance 00756 // stop and record elapsed time, print stats 00757 void NestingAwareController::stop(ID id, bool count) { 00758 boost::mutex::scoped_lock lock(mutex); 00759 Stat& st = instrumentations[id]; 00760 00761 Time now = boost::posix_time::microsec_clock::local_time(); 00762 00763 // currently active instrumentation 00764 Current& c = current.back(); 00765 if( id != c.which ) 00766 // warn but ignore 00767 std::cerr << "inconsistency: instrumentation '" << st.name << 00768 "' currently running but got stop() for '" << instrumentations[id].name << 00769 "'! (might cause subsequent errors)" << std::endl; 00770 00771 // update pure duration 00772 st.pureDuration += now - c.start; 00773 00774 // update overall duration, but only if not nested with itself 00775 // (this way neither pure nor overall duration is counted twice) 00776 Duration thisDuration; 00777 if( st.level == 1 ) { 00778 // the continuously logged duration must be non-pure, otherwise we need 00779 // to separately accumulate pureDuration since most recent start() 00780 thisDuration = now - c.firststart; 00781 st.duration += thisDuration; 00782 } 00783 if( count ) { 00784 st.count++; 00785 printInformationContinous(st, thisDuration); 00786 } 00787 00788 // remove current instrumentation 00789 current.pop_back(); 00790 st.level--; 00791 00792 // leave function if there is no higher-level instrumentation running 00793 if( current.empty() ) 00794 return; 00795 00796 // resume previous instrumentation 00797 Current& prevc = current.back(); 00798 prevc.start = now; 00799 } 00800 00801 // record count (no time), print stats 00802 // inline for performance 00803 void NestingAwareController::count(ID id, Count increment) { 00804 boost::mutex::scoped_lock lock(mutex); 00805 Stat& s = instrumentations[id]; 00806 s.count += increment; 00807 printInformationContinous(s,Duration()); 00808 } 00809 00810 } // namespace nestingAware 00811 00812 #if defined(DLVHEX_BENCHMARK_SIMPLE) 00813 typedef simple::BenchmarkController BenchmarkController; 00814 #elif defined(DLVHEX_BENCHMARK_NESTINGAWARE) 00815 typedef nestingAware::NestingAwareController BenchmarkController; 00816 #else 00817 # error Benchmarking is defined but neither SIMPLE nor NESTINGAWARE is chosen! 00818 #endif 00819 00820 } // namespace benchmark 00821 00822 00823 DLVHEX_NAMESPACE_END 00824 #endif // DLVHEX_H_BENCHMARKING_INCLUDED_1555 00825 00826 00827 // vim:expandtab:ts=4:sw=4: 00828 // mode: C++ 00829 // End: