diff --git a/config/src/config.hh.in b/config/src/config.hh.in
index ad74515eed102323bdeaac9f70ac651daa6a7a0d..66e6b5e4c9ccf0c35818da5423668d624221870e 100644
--- a/config/src/config.hh.in
+++ b/config/src/config.hh.in
@@ -17,7 +17,6 @@
 #define PROMOD3_VERSION_STRING "@PROMOD3_VERSION_STRING@"
 
 // control profiling level: 0 = none, else up to that level
-// note: overhead of profiling is not measurable
 // PM3_RUNTIME_PROFILING_LEVEL fetched from top level CMakeLists.txt
 #define PM3_RUNTIME_PROFILING_LEVEL @PM3_RUNTIME_PROFILING_LEVEL@
 
@@ -26,7 +25,7 @@ namespace promod3 {
 /// \brief Get path to the shared data folder. Used for binaries.
 /// This is taken from the env. variable PROMOD3_SHARED_DATA_PATH.
 /// If the emv. var. is not set, it uses the compile-time location as fallback.
-String GetProMod3SharedDataPath() {
+inline String GetProMod3SharedDataPath() {
   char* my_path = getenv("PROMOD3_SHARED_DATA_PATH");
   if (my_path) {
     return String(my_path);
@@ -37,7 +36,7 @@ String GetProMod3SharedDataPath() {
 
 /// \brief Get location of a binary in a subpath of the shared data folder.
 /// Throws an exception and logs if it fails to find the desired file.
-String GetProMod3BinaryPath(const String& subpath, const String& filename) {
+inline String GetProMod3BinaryPath(const String& subpath, const String& filename) {
   boost::filesystem::path shared_path(GetProMod3SharedDataPath());
   boost::filesystem::path my_path = shared_path / subpath / filename;
   if (boost::filesystem::is_regular_file(my_path)) {
diff --git a/core/doc/index.rst b/core/doc/index.rst
index ba72bef45e8e6ba87853104180dc66bf873dc79a..6b1a5ad17be45123b768358c140ee57c163aba60 100644
--- a/core/doc/index.rst
+++ b/core/doc/index.rst
@@ -129,11 +129,14 @@ Runtime profiling
     :param level: Timer only stopped if level <= ``PM3_RUNTIME_PROFILING_LEVEL``
     :type level:  :class:`int`
 
-  .. staticmethod:: PrintSummary()
+  .. staticmethod:: PrintSummary(max_to_show=-1)
 
     Display a summary of all timed entities. The timers are collected by id
     and sorted by total runtime.
 
+    :param max_to_show: if >= 0, show only top `max_to_show` entries.
+    :type max_to_show:  :class:`int`
+
   .. staticmethod:: IsEnabled()
 
     :return: True, if ``PM3_RUNTIME_PROFILING_LEVEL`` > 0. Otherwise, profiling
diff --git a/core/pymod/export_runtime_profiling.cc b/core/pymod/export_runtime_profiling.cc
index 5e25ceb86303415063de070634d692f02078318c..f569123fa9ffbd298e686651735d84a615688b0c 100644
--- a/core/pymod/export_runtime_profiling.cc
+++ b/core/pymod/export_runtime_profiling.cc
@@ -13,8 +13,8 @@ bool IsEnabled() { return (PM3_RUNTIME_PROFILING_LEVEL > 0); }
 
 void export_runtime_profiling() {
   
-  class_<ScopedRuntimeProfiler>("ScopedRuntimeProfiler", no_init);
-  register_ptr_to_python<ScopedRuntimeProfilerPtr>();
+  class_<ScopedTimer>("ScopedTimer", no_init);
+  register_ptr_to_python<ScopedTimerPtr>();
 
   class_<StaticRuntimeProfiler>("StaticRuntimeProfiler", no_init)
     .def("Start", &StaticRuntimeProfiler::Start, (arg("id"), arg("level")=1))
@@ -24,7 +24,8 @@ void export_runtime_profiling() {
     .staticmethod("StartScoped")
     .def("Stop", &StaticRuntimeProfiler::Stop, (arg("level")=1))
     .staticmethod("Stop")
-    .def("PrintSummary", &StaticRuntimeProfiler::PrintSummary)
+    .def("PrintSummary", &StaticRuntimeProfiler::PrintSummary,
+         (arg("max_to_show")=-1))
     .staticmethod("PrintSummary")
     .def("IsEnabled", &IsEnabled)
     .staticmethod("IsEnabled")
diff --git a/core/src/runtime_profiling.hh b/core/src/runtime_profiling.hh
index 3f6d2c428840312ee4d5facf64ec7477112aa160..7d1df5c4117992672ac00c53f4d582066cafc995 100644
--- a/core/src/runtime_profiling.hh
+++ b/core/src/runtime_profiling.hh
@@ -2,6 +2,19 @@
 /// All profiling can be completely turned off by setting 
 /// PM3_RUNTIME_PROFILING_LEVEL=0
 
+// timing for 1000000 ScopedTimer timings
+// -> def = time loop where we just do Start/Stop (compiled with -O2)
+//    error = measured time between Start/Stop w/o doing anything
+//    lowlevel = level > PM3_RUNTIME_PROFILING_LEVEL
+//    disabled = PM3_RUNTIME_PROFILING_LEVEL==0
+// - from C++:    Start/Stop: 0.12s , StartScoped: 0.23s
+//   -> error:    Start/Stop: 0.047s, StartScoped: 0.09s
+//   -> disabled or lowlevel: 0s (with -O2 or more)
+// - from Python: Start/Stop: 0.8s , StartScoped: 0.8s
+//   -> error:    Start/Stop: 0.32s, StartScoped: 0.22s
+//   -> lowlevel: Start/Stop: 0.65s, StartScoped: 0.38s
+//   -> disabled: same as lowlevel
+
 #ifndef PROMOD3_RUNTIME_PROFILING_HH
 #define PROMOD3_RUNTIME_PROFILING_HH
 
@@ -15,17 +28,18 @@
 
 namespace promod3 { namespace core {
 
-class ScopedRuntimeProfiler { };
-typedef boost::shared_ptr<ScopedRuntimeProfiler> ScopedRuntimeProfilerPtr;
+class ScopedTimer { };
+typedef boost::shared_ptr<ScopedTimer> ScopedTimerPtr;
 
+// NOTE: only this interface guaranteed to be available
 class StaticRuntimeProfiler {
 public:
-  static void Start(const String& id, int level=1) { }
-  static ScopedRuntimeProfilerPtr StartScoped(const String& id, int level=1) {
-    return ScopedRuntimeProfilerPtr();
+  static void Start(const char* id, int level = 1) { }
+  static ScopedTimerPtr StartScoped(const char* id, int level = 1) {
+    return ScopedTimerPtr();
   }
-  static void Stop(int level=1) { }
-  static void PrintSummary() { }
+  static void Stop(int level = 1) { }
+  static void PrintSummary(int max_to_show = -1) { }
 };
 
 }} // ns
@@ -98,8 +112,8 @@ private:
 };
 
 // fw decl. needed for RuntimeProfiler
-class ScopedRuntimeProfiler;
-typedef boost::shared_ptr<ScopedRuntimeProfiler> ScopedRuntimeProfilerPtr;
+class ScopedTimer;
+typedef boost::shared_ptr<ScopedTimer> ScopedTimerPtr;
 
 /// \brief Keep track of multiple profiled entried (identified by a String).
 ///
@@ -134,7 +148,7 @@ public:
     watches_.back().second.Start();
   }
 
-  ScopedRuntimeProfilerPtr StartScoped(const String& id);
+  ScopedTimerPtr StartScoped(const String& id);
 
   // note: no check done! Something must have been running!
   void Stop() {
@@ -149,7 +163,7 @@ public:
     if (!watches_.empty()) watches_.back().second.Start();
   }
 
-  void PrintSummary() {
+  void PrintSummary(int max_to_show = -1) {
     if (profile_entries_.empty()) return;
     // get total measured runtime
     Real total_runtime = 0;
@@ -182,9 +196,13 @@ public:
     std::cout << String(max_id_width + 28, '=') << std::endl;
     std::cout << tmp << ": num.runs, tot.(s), percent\n";
     std::cout << String(max_id_width + 28, '-') << std::endl;
+    int shown = 0;
     for (std::map<Real, String>::const_reverse_iterator it
          = sorted_entries.rbegin(); it != sorted_entries.rend(); ++it) {
-      std::cout << it->second << std::endl;
+      if (max_to_show < 0 || shown < max_to_show) {
+        std::cout << it->second << std::endl;
+        ++shown;
+      }
     }
   }
 
@@ -201,43 +219,43 @@ private:
 };
 
 /// \brief Helper for StartScoped
-class ScopedRuntimeProfiler {
+class ScopedTimer {
 public:
-  ScopedRuntimeProfiler(RuntimeProfiler* rp, const String& id) {
+  ScopedTimer(RuntimeProfiler* rp, const String& id) {
     rp_ = rp;
     rp->Start(id);
   }
-  ~ScopedRuntimeProfiler() {
+  ~ScopedTimer() {
     rp_->Stop();
   }
 private:
   RuntimeProfiler* rp_;
 };
 
-// circular use RuntimeProfiler<->ScopedRuntimeProfiler, this must be here!
-inline ScopedRuntimeProfilerPtr RuntimeProfiler::StartScoped(const String& id) {
-  return ScopedRuntimeProfilerPtr(new ScopedRuntimeProfiler(this, id));
+// circular use RuntimeProfiler<->ScopedTimer, this must be here!
+inline ScopedTimerPtr RuntimeProfiler::StartScoped(const String& id) {
+  return ScopedTimerPtr(new ScopedTimer(this, id));
 }
 
 /// \brief Wrapper for RuntimeProfiler with static access.
 /// This makes sure we can remove all profiling stuff at compile-time.
 class StaticRuntimeProfiler {
 public:
-  static void Start(const String& id, int level=1) {
+  static void Start(const char* id, int level = 1) {
     if (level <= PM3_RUNTIME_PROFILING_LEVEL) { GetInstance_().Start(id); }
   }
-  static ScopedRuntimeProfilerPtr StartScoped(const String& id, int level=1) {
+  static ScopedTimerPtr StartScoped(const char* id, int level = 1) {
     if (level <= PM3_RUNTIME_PROFILING_LEVEL) { 
       return GetInstance_().StartScoped(id);
     } else {
-      return ScopedRuntimeProfilerPtr();
+      return ScopedTimerPtr();
     }
   }
   static void Stop(int level=1) {
     if (level <= PM3_RUNTIME_PROFILING_LEVEL) { GetInstance_().Stop(); }
   }
-  static void PrintSummary() {
-    GetInstance_().PrintSummary();
+  static void PrintSummary(int max_to_show = -1) {
+    GetInstance_().PrintSummary(max_to_show);
   }
 private:
   // singleton access to one profiler instance.
diff --git a/loop/src/backbone_relaxer.cc b/loop/src/backbone_relaxer.cc
index 253c366842d8ee3ab42d02713d8f1a69108484cc..912b71d330cd746cb8c39b95d565142b33a9a79d 100644
--- a/loop/src/backbone_relaxer.cc
+++ b/loop/src/backbone_relaxer.cc
@@ -2,7 +2,7 @@
 #include <ost/log.hh>
 #include <ost/version.hh>
 #include "promod3/loop/backbone_relaxer.hh"
-
+#include <promod3/core/runtime_profiling.hh>
 
 namespace promod3 { namespace loop {
 
@@ -24,6 +24,9 @@ BackboneRelaxer::BackboneRelaxer(const BackboneList& bb_list,
 
 Real BackboneRelaxer::Run(BackboneList& bb_list, int steps, Real stop_criterion){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "BackboneRelaxer::Run", 2);
+
   if(bb_list.size() != s_.size()){
     std::stringstream ss;
     ss << "Size of bblist is not consistent with the size the optimizer has ";
diff --git a/loop/src/ccd.cc b/loop/src/ccd.cc
index a4aab643fd86ca626e63f8eb16ae45e82eda5614..635cc20cbac96b47d19704853f230912be55e64a 100644
--- a/loop/src/ccd.cc
+++ b/loop/src/ccd.cc
@@ -1,4 +1,5 @@
 #include <promod3/loop/ccd.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace {
 
@@ -196,6 +197,9 @@ bool CCD::Close(BackboneList& bb_list){
 
 bool CCD::SimpleClose(BackboneList& bb_list){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "CCD::SimpleClose", 2);
+
   SuperposeOntoNStem(bb_list,n_stem_);
   FitCStem(target_positions_,bb_list);
 
@@ -283,6 +287,10 @@ bool CCD::SimpleClose(BackboneList& bb_list){
 }
 
 bool CCD::ConstrainedClose(promod3::loop::BackboneList& bb_list){
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "CCD::ConstrainedClose", 2);
+  
   if(bb_list.size() != torsion_sampler_.size()){
     std::stringstream ss;
     ss << "Size of BackboneList must be consistent with the sequence you ";
diff --git a/loop/src/kic.cc b/loop/src/kic.cc
index a3ce1575542f905cb4610eea62cec20ff865cf88..1ef48efde12e13a7ac91282a50762532739ef51b 100644
--- a/loop/src/kic.cc
+++ b/loop/src/kic.cc
@@ -1,4 +1,5 @@
 #include <promod3/loop/kic.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace{
 
@@ -422,8 +423,11 @@ KIC::KIC(const ost::mol::ResidueHandle& n_stem,
          const ost::mol::ResidueHandle& c_stem): n_stem_(n_stem),
                                                  c_stem_(c_stem) { }
 
-void KIC::Close(const BackboneList& bb_list, uint pivot_one, uint pivot_two, uint pivot_three,
-                std::vector<BackboneList>& solutions) const{
+void KIC::Close(const BackboneList& bb_list, uint pivot_one, uint pivot_two,
+                uint pivot_three, std::vector<BackboneList>& solutions) const {
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "KIC::Close", 2);
 
   if(pivot_one < 1 || pivot_three >= bb_list.size()-1 || pivot_one >= pivot_two || pivot_two >= pivot_three){
     throw promod3::Error("Observed invalid pivot residues when running KIC loop closing algorithm!");
diff --git a/loop/src/loop_candidate.cc b/loop/src/loop_candidate.cc
index 9135d6706abbc64bf5592e137ac4e1c461478e94..1886f679159054b6679e9de0cc247b5c8334df11 100644
--- a/loop/src/loop_candidate.cc
+++ b/loop/src/loop_candidate.cc
@@ -1,4 +1,5 @@
 #include <promod3/loop/loop_candidate.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace promod3 { namespace loop {
 
@@ -57,6 +58,9 @@ LoopCandidatesPtr LoopCandidates::FillFromDatabase(const ost::mol::ResidueHandle
                                                    StructureDBPtr structure_db,
                                                    bool extended_search){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "LoopCandidates::FillFromDatabase", 2);
+
   LoopCandidatesPtr candidates(new LoopCandidates(seq));
   std::vector<FragmentInfo> fragments;
   frag_db->SearchDB(n_stem,c_stem,seq.size(),fragments,extended_search);
@@ -91,6 +95,9 @@ LoopCandidatesPtr LoopCandidates::FillFromMonteCarloSampler(const String& seq,
                                                             MonteCarloCoolerPtr cooler,
                                                             int random_seed){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "LoopCandidates::FillFromMonteCarloSampler", 2);
+
   LoopCandidatesPtr candidates(new LoopCandidates(seq));
 
   for(uint i = 0; i < num_loops; ++i){
@@ -113,6 +120,9 @@ LoopCandidatesPtr LoopCandidates::FillFromMonteCarloSampler(const BackboneList&
                                                             MonteCarloCoolerPtr cooler,
                                                             int random_seed){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "LoopCandidates::FillFromMonteCarloSampler", 2);
+
   //check seq and initial conformation for consistency
   if(seq.size() != initial_bb.size()){
     throw promod3::Error("Sequence and initial conformation are inconsistent in size!");
@@ -141,7 +151,9 @@ void LoopCandidates::ApplyCCD(const ost::mol::ResidueHandle& n_stem,
                               const ost::mol::ResidueHandle& c_stem,
                               std::vector<TorsionSamplerPtr> torsion_sampler, 
                               int max_iterations, Real rmsd_cutoff, 
-                              bool keep_non_converged, int random_seed){
+                              bool keep_non_converged, int random_seed) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "LoopCandidates::ApplyCCD", 2);
   CCD closer(sequence_,n_stem,c_stem,torsion_sampler,max_iterations,rmsd_cutoff,random_seed);
   for(LoopCandidateList::iterator i = this->begin(); i != this->end(); ){
     if(!closer.Close(i->bb_list) && !keep_non_converged){
@@ -156,7 +168,9 @@ void LoopCandidates::ApplyCCD(const ost::mol::ResidueHandle& n_stem,
                               const ost::mol::ResidueHandle& c_stem,
                               TorsionSamplerPtr torsion_sampler, 
                               int max_iterations, Real rmsd_cutoff, 
-                              bool keep_non_converged, int random_seed){
+                              bool keep_non_converged, int random_seed) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "LoopCandidates::ApplyCCD", 2);
   CCD closer(sequence_,n_stem,c_stem,torsion_sampler,max_iterations,rmsd_cutoff,random_seed);
   for(LoopCandidateList::iterator i = this->begin(); i != this->end(); ){
     if(!closer.Close(i->bb_list) && !keep_non_converged){
@@ -170,7 +184,9 @@ void LoopCandidates::ApplyCCD(const ost::mol::ResidueHandle& n_stem,
 void LoopCandidates::ApplyCCD(const ost::mol::ResidueHandle& n_stem,
                               const ost::mol::ResidueHandle& c_stem,
                               int max_iterations, Real rmsd_cutoff, 
-                              bool keep_non_converged){
+                              bool keep_non_converged) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "LoopCandidates::ApplyCCD", 2);
   CCD closer(n_stem,c_stem,max_iterations,rmsd_cutoff);
   for(LoopCandidateList::iterator i = this->begin(); i != this->end(); ){
     if(!closer.Close(i->bb_list) && !keep_non_converged){
@@ -183,8 +199,9 @@ void LoopCandidates::ApplyCCD(const ost::mol::ResidueHandle& n_stem,
 
 void LoopCandidates::ApplyKIC(const ost::mol::ResidueHandle& n_stem,
                               const ost::mol::ResidueHandle& c_stem,
-                              uint pivot_one, uint pivot_two, uint pivot_three){
-
+                              uint pivot_one, uint pivot_two, uint pivot_three) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "LoopCandidates::ApplyKIC", 2);
   LoopCandidateList new_candidates;
   KIC closer(n_stem, c_stem);
 
diff --git a/loop/src/loop_object_loader.cc b/loop/src/loop_object_loader.cc
index eec449c471c96937928ceea7175fb13ad66b8d94..22c917fff09bc0a617ecbfdcfcab806c26a2ab5f 100644
--- a/loop/src/loop_object_loader.cc
+++ b/loop/src/loop_object_loader.cc
@@ -1,46 +1,61 @@
 #include <promod3/loop/loop_object_loader.hh>
 #include <promod3/config.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace promod3{ namespace loop{
 
 
-TorsionSamplerPtr LoadTorsionSampler(uint seed){
+TorsionSamplerPtr LoadTorsionSampler(uint seed) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "loop::LoadTorsionSampler", 2);
   String path = GetProMod3BinaryPath("loop_data", "torsion_sampler.dat");
   TorsionSamplerPtr p = TorsionSampler::Load(path,seed);
   return p;
 }
 
-TorsionSamplerPtr LoadTorsionSamplerHelical(uint seed){
+TorsionSamplerPtr LoadTorsionSamplerHelical(uint seed) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "loop::LoadTorsionSamplerHelical", 2);
   String path = GetProMod3BinaryPath("loop_data", "torsion_sampler_helical.dat");
   TorsionSamplerPtr p = TorsionSampler::Load(path,seed);
   return p;
 }
 
-TorsionSamplerPtr LoadTorsionSamplerExtended(uint seed){
+TorsionSamplerPtr LoadTorsionSamplerExtended(uint seed) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "loop::LoadTorsionSamplerExtended", 2);
   String path = GetProMod3BinaryPath("loop_data", "torsion_sampler_extended.dat");
   TorsionSamplerPtr p = TorsionSampler::Load(path,seed);
   return p;
 }
 
-TorsionSamplerPtr LoadTorsionSamplerCoil(uint seed){
+TorsionSamplerPtr LoadTorsionSamplerCoil(uint seed) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "loop::LoadTorsionSamplerCoil", 2);
   String path = GetProMod3BinaryPath("loop_data", "torsion_sampler_coil.dat");
   TorsionSamplerPtr p = TorsionSampler::Load(path,seed);
   return p;
 }
 
-FragDBPtr LoadFragDB(){
+FragDBPtr LoadFragDB() {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "loop::LoadFragDB", 2);
   String path = GetProMod3BinaryPath("loop_data", "frag_db.dat");
   FragDBPtr p = FragDB::Load(path);
   return p; 
 }
 
-StructureDBPtr LoadStructureDB(bool load_frequencies){
+StructureDBPtr LoadStructureDB(bool load_frequencies) {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "loop::LoadStructureDB", 2);
   String path = GetProMod3BinaryPath("loop_data", "structure_db.dat");
   StructureDBPtr p = StructureDB::Load(path, load_frequencies);
   return p; 
 }
 
-BackboneLoopScorerPtr LoadBackboneLoopScorer(){
+BackboneLoopScorerPtr LoadBackboneLoopScorer() {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "loop::LoadBackboneLoopScorer", 2);
   String path = GetProMod3BinaryPath("loop_data", "backbone_loop_scorer.dat");
   BackboneLoopScorerPtr p = BackboneLoopScorer::Load(path);
   return p;  
diff --git a/loop/src/torsion_sampler.cc b/loop/src/torsion_sampler.cc
index b7e64a601638955c0cf2df24ff26e3d3fd431a36..05fafe1fa97d24b27ecb4ecfd9700aff2e612b5f 100644
--- a/loop/src/torsion_sampler.cc
+++ b/loop/src/torsion_sampler.cc
@@ -1,12 +1,14 @@
 #include <promod3/loop/torsion_sampler.hh>
 #include <promod3/core/check_io.hh>
 #include <promod3/core/portable_binary_serializer.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace promod3{ namespace loop{
 
 TorsionSampler::TorsionSampler(const std::vector<String>& group_definitions, 
                                int bin_size, uint seed): gen_(seed){
-  
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "TorsionSampler::TorsionSampler", 2);
   String group_key;
   std::vector<String> single_ids(3);
   for(int i = 0; i < ost::conop::XXX; ++i){
@@ -481,7 +483,9 @@ std::vector<uint> TorsionSampler::GetHistogramIndices(const String& sequence){
   return ret_value;
 }
 
-void TorsionSampler::UpdateDistributions(){
+void TorsionSampler::UpdateDistributions() {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "TorsionSampler::UpdateDistributions", 2);
   //fill phi/psi stuff
   for(uint i = 0; i < distributions_.size(); ++i){
     distributions_[i] = boost::random::discrete_distribution<int,Real>(histogram_[i].begin(),histogram_[i].end());
@@ -522,6 +526,8 @@ void TorsionSampler::UpdateDistributions(){
 }
 
 void TorsionSampler::SetHistogram(const std::vector<std::vector<int> >& histogram){
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "TorsionSampler::SetHistogram", 2);
   if(histogram.size() != histogram_.size()){
     throw promod3::Error("Error in loading TorsionSampler");
   }
diff --git a/modelling/pymod/CMakeLists.txt b/modelling/pymod/CMakeLists.txt
index 36f7c6897e5d1fbcff56a914eaa36265a062c265..2e534bf081680aa9c6c94b812826765d42173599 100644
--- a/modelling/pymod/CMakeLists.txt
+++ b/modelling/pymod/CMakeLists.txt
@@ -17,4 +17,4 @@ set(MODELLING_PYMOD
 pymod(NAME modelling
       CPP ${MODELLING_CPP}
       PY ${MODELLING_PYMOD}
-      DEPENDS_ON _loop _sidechain)
+      DEPENDS_ON _core _loop _sidechain)
diff --git a/modelling/pymod/_closegaps.py b/modelling/pymod/_closegaps.py
index fa974adb3b81fd29830ce6aaa047d45fd93197bc..c071fa8394e7b4dbadbeff370291e369b067e034 100644
--- a/modelling/pymod/_closegaps.py
+++ b/modelling/pymod/_closegaps.py
@@ -4,7 +4,7 @@ as argument.
 '''
 
 # internal
-from promod3 import loop, sidechain
+from promod3 import loop, sidechain, core
 from _modelling import *
 from _ring_punches import *
 # external
@@ -206,6 +206,7 @@ def _CloseLoop(mhandle, scorer, gap_orig, actual_candidates,
                     2 = _CloseLoopBare(penalize_length=True)
     :return: gap-index as returned by ClearGaps or -2 if fail.
     '''
+    prof = core.StaticRuntimeProfiler.StartScoped('closegaps::_CloseLoop')
     # check consistency
     N = len(actual_extended_gaps)
     if len(actual_candidates) != N:
@@ -263,6 +264,8 @@ def SetupBackboneScorer(mhandle):
     :return: A scorer instance.
     :rtype: :class:`~promod3.loop.BackboneLoopScorer`
     '''
+    prof_name = 'closegaps::SetupBackboneScorer'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     scorer = loop.LoadBackboneLoopScorer()
     scorer.Initialize(mhandle.seqres)
     scorer.SetEnvironment(mhandle.model)
@@ -329,6 +332,9 @@ def CloseSmallDeletions(mhandle, scorer, max_extension=9, clash_thresh=1.0,
     :type resnum_range: :class:`tuple` containing two :class:`int`
     '''
 
+    prof_name = 'closegaps::CloseSmallDeletions'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
+
     if len(mhandle.gaps) > 0:
         ost.LogInfo("Trying to close small deletions (no. of gap(s): %d)." % \
                        len(mhandle.gaps))
@@ -436,6 +442,9 @@ def MergeGapsByDistance(mhandle, distance):
     :type distance: :class:`int`
     '''
 
+    prof_name = 'closegaps::MergeGapsByDistance'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
+
     if len(mhandle.gaps) > 1:
         ost.LogInfo("Trying to merge %d gap(s) with distance %d." % \
                        (len(mhandle.gaps), distance))
@@ -572,6 +581,9 @@ def FillLoopsByDatabase(mhandle, scorer, fragment_db, structure_db,
     :type resnum_range: :class:`tuple` containing two :class:`int`
     '''
 
+    prof_name = 'closegaps::FillLoopsByDatabase'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
+
     if len(mhandle.gaps) > 0:
         ost.LogInfo("Trying to fill %d gap(s) by database." % len(mhandle.gaps))
     else:
@@ -773,6 +785,9 @@ def FillLoopsByMonteCarlo(mhandle, scorer, torsion_sampler, max_loops_to_search=
     :type resnum_range: :class:`tuple` containing two :class:`int`
     '''
 
+    prof_name = 'closegaps::FillLoopsByMonteCarlo'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
+
     if len(mhandle.gaps) > 0:
         ost.LogInfo("Trying to fill %d gap(s) by Monte Carlo." % len(mhandle.gaps))
     else:
@@ -953,6 +968,9 @@ def ModelTermini(mhandle, scorer, torsion_sampler, fragger_handles=None,
     :type mc_steps:  :class:`int`
     '''
 
+    prof_name = 'closegaps::ModelTermini'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
+
     # get terminal gaps (copies as we'll clear them as we go)
     terminal_gaps = [g.Copy() for g in mhandle.gaps if g.IsTerminal() and g.length > 1]
     if len(terminal_gaps) > 0:
@@ -1084,6 +1102,9 @@ def CloseLargeDeletions(mhandle, scorer, structure_db, linker_length=8,
     :type use_full_extender:  :class:`bool`
     '''
 
+    prof_name = 'closegaps::CloseLargeDeletions'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
+
     if len(mhandle.gaps) > 0:
         ost.LogInfo("Trying to resolve large deletions (%d gap(s) left) by "
                     "sampling a linker region." % len(mhandle.gaps))
diff --git a/modelling/pymod/_pipeline.py b/modelling/pymod/_pipeline.py
index 5f059d529702bc81f9a11871ac469b8b62fa2ca7..1be19a1e06834a94b986266ae57b0593dfff83d6 100644
--- a/modelling/pymod/_pipeline.py
+++ b/modelling/pymod/_pipeline.py
@@ -4,8 +4,7 @@ as argument.
 '''
 
 # internal
-from promod3 import loop
-from promod3 import sidechain
+from promod3 import loop, sidechain, core
 from _modelling import *
 from _closegaps import *
 from _ring_punches import *
@@ -101,6 +100,8 @@ def _SetupMmSimulation(model, force_fields):
     component in the ligand chain separately by evaluating the force fields in
     the same order as given. Ligands without force fields are skipped.
     '''
+    prof = core.StaticRuntimeProfiler.StartScoped('pipeline::_SetupMmSimulation')
+
     # get general settings 
     settings = mm.Settings()
     settings.integrator = mm.LangevinIntegrator(310, 1, 0.002)
@@ -193,6 +194,7 @@ def BuildSidechains(mhandle, merge_distance=4, scorer=None, fragment_db=None,
                             if None.
     :type torsion_sampler:  :class:`~promod3.loop.TorsionSampler`
     '''
+    prof = core.StaticRuntimeProfiler.StartScoped('pipeline::BuildSidechains')
     ost.LogInfo("Rebuilding sidechains.")
     sidechain.Reconstruct(mhandle.model, keep_sidechains=True)
     # check for ring punches
@@ -272,6 +274,7 @@ def MinimizeModelEnergy(mhandle, max_iterations=12, max_iter_sd=20,
     :return: The model including all oxygens as used in the minimizer.
     :rtype:  :class:`Entity <ost.mol.EntityHandle>`
     '''
+    prof = core.StaticRuntimeProfiler.StartScoped('pipeline::MinimizeModelEnergy')
     ost.LogInfo("Minimize energy.")
     # ignore LogInfo in stereochemical problems if output up to info done
     ignore_stereo_log = (ost.GetVerbosityLevel() == 3)
@@ -333,6 +336,8 @@ def CheckFinalModel(mhandle):
     :param mhandle: Modelling handle for which to perform checks.
     :type mhandle:  :class:`ModellingHandle`
     '''
+    prof_name = 'pipeline::CheckFinalModel'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     # report incomplete models
     if len(mhandle.gaps) > 0:
         ost.LogWarning("Failed to close %d gap(s). Returning incomplete model!" % \
@@ -424,6 +429,8 @@ def BuildFromRawModel(mhandle, use_amber_ff=False, extra_force_fields=list()):
     :return: Delivers the model as an |ost_s| entity.
     :rtype: :class:`Entity <ost.mol.EntityHandle>`
     '''
+    prof_name = 'pipeline::BuildFromRawModel'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     ost.LogInfo("Starting modelling based on a raw model.")
 
     # a bit of setup
diff --git a/modelling/pymod/_ring_punches.py b/modelling/pymod/_ring_punches.py
index 19bd4dd864dc1de260b05d19f66a65753674c3c2..65c5d249dbf2432083ccd0a8fe98616270370967 100644
--- a/modelling/pymod/_ring_punches.py
+++ b/modelling/pymod/_ring_punches.py
@@ -1,7 +1,7 @@
 '''Helper functions to deal with ring punchings.'''
 import ost
 from ost import geom
-from promod3 import sidechain
+from promod3 import sidechain, core
 from collections import namedtuple
 
 def _AddRing(rings, res, atom_names):
@@ -115,6 +115,8 @@ def GetRingPunches(rings, ent):
     :return: :class:`list` of residues (:class:`~ost.mol.ResidueHandle`) which
               have a punched ring.
     '''
+    prof_name = 'ring_punches::GetRingPunches'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     ring_punches = list()
     for ring in rings:
         # we don't need to add residues multiple times
@@ -138,6 +140,8 @@ def HasRingPunches(rings, ent):
     :return: True, iff any ring is punched
     :rtype:  :class:`bool`
     '''
+    prof_name = 'ring_punches::HasRingPunches'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     for ring in rings:
         # check neighborhood (3A should be enough)
         for atom in ent.FindWithin(ring.center, 3):
@@ -156,6 +160,8 @@ def FilterCandidates(candidates, model, gap):
     :param gap:        Gap for which loop is to be filled.
     :type gap:         :class:`StructuralGap`.
     '''
+    prof_name = 'ring_punches::FilterCandidates'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     start_resnum = gap.before.GetNumber()
     chain_idx = gap.GetChainIndex()
     # precompute rings and range of rings to replace
@@ -184,6 +190,8 @@ def FilterCandidatesWithSC(candidates, model, gap):
     '''Remove loop candidates if they (with sidechain) cause ring punches.
     See :func:`FilterCandidates`.
     '''
+    prof_name = 'ring_punches::FilterCandidatesWithSC'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     start_resnum = gap.before.GetNumber()
     chain_idx = gap.GetChainIndex()
     cur_model = model.Copy()
diff --git a/modelling/src/model.cc b/modelling/src/model.cc
index 2614d87c9e768b9e2afcfa82a86f6044b3c5ad75..986422bb637db613ce18db23a303662160a71f59 100644
--- a/modelling/src/model.cc
+++ b/modelling/src/model.cc
@@ -10,6 +10,7 @@
 #include <ost/conop/conop.hh>
 #include <ost/conop/compound_lib.hh>
 #include <promod3/core/geom_base.hh>
+#include <promod3/core/runtime_profiling.hh>
 #include "model.hh"
 
 using namespace ost::mol;
@@ -51,6 +52,9 @@ bool CheckBackboneAtoms(ResidueView res)
 int CountEnclosedGaps(ModellingHandle& mhandle, const StructuralGap& gap,
                       bool insertions_only)
 {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "modelling::CountEnclosedGaps", 2);
+
   int num_gaps = 0;
   
   // extract info for this gap
@@ -80,6 +84,9 @@ int CountEnclosedGaps(ModellingHandle& mhandle, const StructuralGap& gap,
 
 int ClearGaps(ModellingHandle& mhandle, const StructuralGap& gap) {
   
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "modelling::ClearGaps", 2);
+
   // return -1 if no more gaps
   int next_gap = -1;
   
@@ -129,6 +136,9 @@ int ClearGaps(ModellingHandle& mhandle, const StructuralGap& gap) {
 
 void MergeGaps(ModellingHandle& mhandle, uint index) {
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "modelling::MergeGaps", 2);
+
   if(index >= mhandle.gaps.size()-1){
     throw promod3::Error("Invalid index provided when merging gaps!");
   }
@@ -176,6 +186,10 @@ void MergeGaps(ModellingHandle& mhandle, uint index) {
 }
 
 int RemoveTerminalGaps(ModellingHandle& mhandle) {
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "modelling::RemoveTerminalGaps", 2);
+
   int removed_gaps = 0;
   for(StructuralGapList::iterator i = mhandle.gaps.begin(); i != mhandle.gaps.end(); ){
     if(i->IsNTerminal() || i->IsCTerminal()){
@@ -393,6 +407,9 @@ ModellingHandle BuildRawModel(const ost::seq::AlignmentList& aln,
                               const String& chain_names,
                               bool spdbv_style)
 {
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "modelling::BuildRawModel", 2);
+
   ModellingHandle result;
   EntityHandle model=CreateEntity();
   XCSEditor edi=model.EditXCS(BUFFERED_EDIT);
@@ -431,6 +448,10 @@ ModellingHandle BuildRawModel(const ost::seq::AlignmentList& aln,
 void AddLigands(const ost::seq::AlignmentList& aln, XCSEditor& edi,
                 EntityHandle& model)
 {
+  
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "modelling::AddLigands", 2);
+
   // make sure we handle duplicates
   std::set<mol::EntityHandle> unique_handles;
   std::vector<mol::ResidueViewList> ligand_views;
@@ -495,6 +516,10 @@ void BuildRawChain(const ost::seq::AlignmentHandle& aln,
                    char chain_name,
                    bool spdbv_style)
 {
+  
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "modelling::BuildRawChain", 2);
+
   // FIXME: While conseptually simple, this function is way too long for my 
   //        taste. It should be chopped into smaller pieces.
   if (aln.GetCount()!=2) {
diff --git a/modelling/tests/test_close_gaps.py b/modelling/tests/test_close_gaps.py
index 1ecbba26d222450a45ce36b5e2c054372543dde4..7119e5a6a9f65774c920590edad8ea42b9bfdaac 100644
--- a/modelling/tests/test_close_gaps.py
+++ b/modelling/tests/test_close_gaps.py
@@ -4,7 +4,7 @@ Unit tests for modelling components to close gaps.
 import unittest
 import ost
 from ost import io, seq, geom
-from promod3 import loop, modelling
+from promod3 import loop, modelling, core
 
 # setting up an OST LogSink to capture messages
 class _FetchLog(ost.LogSink):
@@ -33,6 +33,11 @@ class CloseGapsTests(unittest.TestCase):
         cls.structure_db = loop.LoadStructureDB()
         cls.torsion_sampler = loop.LoadTorsionSamplerCoil()
 
+    @classmethod
+    def tearDownClass(cls):
+        # show runtimes if this was enabled
+        core.StaticRuntimeProfiler.PrintSummary(5)
+
     #######################################################################
     # HELPERs
     #######################################################################
diff --git a/modelling/tests/test_pipeline.py b/modelling/tests/test_pipeline.py
index 5a218b99c5967d3133e7a9ab71fa80017299b733..ed48f118cbc9c209bf2b577bb5c11ae7523ceaad 100644
--- a/modelling/tests/test_pipeline.py
+++ b/modelling/tests/test_pipeline.py
@@ -6,7 +6,7 @@ import unittest
 import math
 import ost
 from ost import io, mol, seq, settings
-from promod3 import loop, modelling
+from promod3 import loop, modelling, core
 
 ################################################################
 # Code to generate 1crn_cut and 1crn.fasta:
@@ -127,6 +127,11 @@ class PipelineTests(unittest.TestCase):
         cls.structure_db = loop.LoadStructureDB()
         cls.torsion_sampler = loop.LoadTorsionSamplerCoil()
 
+    @classmethod
+    def tearDownClass(cls):
+        # show runtimes if this was enabled
+        core.StaticRuntimeProfiler.PrintSummary(5)
+
     #######################################################################
     # HELPERs
     #######################################################################
diff --git a/sidechain/pymod/CMakeLists.txt b/sidechain/pymod/CMakeLists.txt
index b488f0053171adec2658d53d59b93d389476dee5..8e92310abd9fbdc22da27b5c41bd14bb7b57d468 100644
--- a/sidechain/pymod/CMakeLists.txt
+++ b/sidechain/pymod/CMakeLists.txt
@@ -20,4 +20,5 @@ __init__.py
 _reconstruct_sidechains.py
 )
 
-pymod(NAME sidechain CPP ${SIDECHAIN_CPP} PY ${SIDECHAIN_PYMOD})
+pymod(NAME sidechain CPP ${SIDECHAIN_CPP} PY ${SIDECHAIN_PYMOD}
+      DEPENDS_ON _core)
diff --git a/sidechain/pymod/_reconstruct_sidechains.py b/sidechain/pymod/_reconstruct_sidechains.py
index d5a75d1be87018f44d5a35352efea6e47503a02a..589b00e758e165c7828c2f62e81c656404f942eb 100644
--- a/sidechain/pymod/_reconstruct_sidechains.py
+++ b/sidechain/pymod/_reconstruct_sidechains.py
@@ -1,5 +1,6 @@
 from . import _sidechain as sidechain
 from ost import geom, mol, conop
+from promod3 import core
 
 ###############################################################################
 # helper functions
@@ -58,6 +59,8 @@ def _GetDihedrals(res_list):
     '''Extract dihedral angles for all residues.
     Returns phi and psi angles as 2 lists with length = len(res_list).
     '''
+    prof_name = 'sidechain::_GetDihedrals'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
     phi_angles = [0.0] * len(res_list)
     psi_angles = [0.0] * len(res_list)
     for i,r in enumerate(res_list):
@@ -232,6 +235,9 @@ def _GetRotamerGroups(res_list, rot_ids, indices, rot_lib, rot_settings,
     Rotamer groups are filtered to keep only best ones (given frame).
     Returns list of rotamer groups and list of res. indices they belong to.
     '''
+    prof_name = 'sidechain::_GetRotamerGroups'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
+
     # res.index (res_list[i]) for each modelled sc
     residues_with_rotamer_group = list()
     #  linked to residue in residues_with_rotamer_group
@@ -413,6 +419,8 @@ def Reconstruct(ent, keep_sidechains=False, build_disulfids=True,
     :type consider_ligands: :class:`bool`
     :type rotamer_library: :class:`BBDepRotamerLib` / :class:`RotamerLib`
     '''
+    prof_name = 'sidechain::Reconstruct'
+    prof = core.StaticRuntimeProfiler.StartScoped(prof_name)
 
     # setup settings
     if rotamer_model.lower() == "frm":
diff --git a/sidechain/src/bb_dep_rotamer_lib.cc b/sidechain/src/bb_dep_rotamer_lib.cc
index 357a5ce25858d3006f9c69364a11b2c08e2a2798..b60d9990832557b05a35777c3b9016082101d7eb 100644
--- a/sidechain/src/bb_dep_rotamer_lib.cc
+++ b/sidechain/src/bb_dep_rotamer_lib.cc
@@ -1,5 +1,6 @@
 #include <promod3/sidechain/bb_dep_rotamer_lib.hh>
 #include <promod3/core/check_io.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace{
   bool RotamerLibEntrySorter(const promod3::sidechain::RotamerLibEntry& a, 
@@ -397,6 +398,9 @@ void BBDepRotamerLib::MakeStatic(){
 
 std::pair<RotamerLibEntry*,uint> BBDepRotamerLib::QueryLib(RotamerID id, Real phi, Real psi) const{
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "BBDepRotamerLib::QueryLib", 2);
+
   if(!readonly_){
     throw promod3::Error("Can only read rotamers from static library!");
   }
diff --git a/sidechain/src/frame.cc b/sidechain/src/frame.cc
index 7b4c04be7f87912d1f1a7d8962d90af8fb461f79..ae78314e96a0c3c78d29fa3bafedd54a980b015c 100644
--- a/sidechain/src/frame.cc
+++ b/sidechain/src/frame.cc
@@ -1,5 +1,5 @@
 #include <promod3/sidechain/frame.hh>
-
+#include <promod3/core/runtime_profiling.hh>
 
 namespace{
 
@@ -53,6 +53,9 @@ Frame::Frame(const std::vector<FrameResiduePtr>& frame_residues,
              const std::vector<geom::Transform>& rt_operators): 
              frame_residues_(frame_residues){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "Frame::Frame", 2);
+
   int overall_size = 0;
   for(std::vector<FrameResiduePtr>::const_iterator i = frame_residues_.begin();
       i != frame_residues_.end(); ++i){
@@ -125,6 +128,9 @@ void Frame::SetFrameEnergy(RRMRotamerGroupPtr p) const{
 
 void Frame::AddFrameEnergy(RRMRotamerGroupPtr p) const{
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "Frame::AddFrameEnergy", 2);
+
   overlapping_particles_.clear();
   this->OverlappingPolytopes(p,overlapping_particles_);
 
@@ -162,6 +168,10 @@ void Frame::SetFrameEnergy(FRMRotamerGroupPtr p) const{
 }
 
 void Frame::AddFrameEnergy(FRMRotamerGroupPtr p) const{
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "Frame::AddFrameEnergy", 2);
+
   overlapping_particles_.clear();
   this->OverlappingPolytopes(p,overlapping_particles_);
 
diff --git a/sidechain/src/frame_constructor.cc b/sidechain/src/frame_constructor.cc
index c43fa586e231abd33b03cc7076e65394fe363cad..415b26dbd4cafa90ef08584bd182284ea74624ad 100644
--- a/sidechain/src/frame_constructor.cc
+++ b/sidechain/src/frame_constructor.cc
@@ -1,5 +1,5 @@
 #include <promod3/sidechain/frame_constructor.hh>
-
+#include <promod3/core/runtime_profiling.hh>
 #include <ost/conop/conop.hh>
 
 using namespace ost::conop;
@@ -1136,11 +1136,17 @@ FrameResiduePtr ConstructSidechainFramePHE(const ost::mol::ResidueHandle& res,
 }
 
 
-FrameResiduePtr ConstructBackboneFrameResidue(const geom::Vec3& n_pos, const geom::Vec3& ca_pos, 
-                                              const geom::Vec3& c_pos, const geom::Vec3& o_pos,
-                                              const geom::Vec3& cb_pos, RotamerID id, uint residue_index,
-                                              RotamerSettingsPtr settings, Real phi, bool n_ter,
-                                              bool c_ter){
+FrameResiduePtr ConstructBackboneFrameResidue(const geom::Vec3& n_pos,
+                                              const geom::Vec3& ca_pos, 
+                                              const geom::Vec3& c_pos,
+                                              const geom::Vec3& o_pos,
+                                              const geom::Vec3& cb_pos,
+                                              RotamerID id, uint residue_index,
+                                              RotamerSettingsPtr settings,
+                                              Real phi, bool n_ter, bool c_ter)
+{
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "sidechain::ConstructBackboneFrameResidue", 2);
 
   Real n_charge, h_charge, ca_charge, cb_charge, c_charge, o_charge;
 
@@ -1612,9 +1618,10 @@ FrameResiduePtr ConstructBackboneFrameResidue(const geom::Vec3& n_pos, const geo
 }
 
 
-FrameResiduePtr ConstructBackboneFrameResidue(const ost::mol::ResidueHandle& res, RotamerID id,
-                                               uint residue_index, RotamerSettingsPtr settings, 
-                                               Real phi, bool n_ter, bool c_ter){
+FrameResiduePtr ConstructBackboneFrameResidue(const ost::mol::ResidueHandle& res,
+                                              RotamerID id, uint residue_index,
+                                              RotamerSettingsPtr settings,
+                                              Real phi, bool n_ter, bool c_ter) {
 
   ost::mol::AtomHandle n = res.FindAtom("N");
   ost::mol::AtomHandle ca = res.FindAtom("CA");
@@ -1641,8 +1648,12 @@ FrameResiduePtr ConstructBackboneFrameResidue(const ost::mol::ResidueHandle& res
                                        residue_index,settings,phi,n_ter,c_ter);
 }
 
-FrameResiduePtr ConstructSidechainFrameResidue(const ost::mol::ResidueHandle& res, RotamerID id,
-                                               uint residue_index, RotamerSettingsPtr settings){
+FrameResiduePtr ConstructSidechainFrameResidue(const ost::mol::ResidueHandle& res,
+                                               RotamerID id, uint residue_index,
+                                               RotamerSettingsPtr settings) {
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "sidechain::ConstructSidechainFrameResidue", 2);
 
   switch (id){
     case ARG :{     
@@ -1869,8 +1880,6 @@ FrameResiduePtr ConstructFrameResidueHeuristic(
       // particle was created anyways...
       ++p_idx;
     }
-    
-    // fallback
   }
 
   return boost::make_shared<FrameResidue>(particles, size, residue_index);
diff --git a/sidechain/src/graph.cc b/sidechain/src/graph.cc
index 9b2ff90dac654da447acbd944a816905f618f66a..ddc2ab4d99a5624d64855e9eed65c98b5ac87cff 100644
--- a/sidechain/src/graph.cc
+++ b/sidechain/src/graph.cc
@@ -1,5 +1,6 @@
 #include <promod3/sidechain/graph.hh>
 #include <promod3/sidechain/tree.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace{
 
@@ -307,8 +308,12 @@ Graph::~Graph(){
   }
 }
 
-GraphPtr Graph::CreateFromRRMList(const std::vector<RRMRotamerGroupPtr>& rotamer_groups,
-                                  const std::vector<geom::Transform>& rt_operators){
+GraphPtr Graph::CreateFromRRMList(
+                const std::vector<RRMRotamerGroupPtr>& rotamer_groups,
+                const std::vector<geom::Transform>& rt_operators) {
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "Graph::CreateFromRRMList", 2);
 
   GraphPtr graph(new Graph);
 
@@ -396,8 +401,12 @@ GraphPtr Graph::CreateFromRRMList(const std::vector<RRMRotamerGroupPtr>& rotamer
 }
 
 
-GraphPtr Graph::CreateFromFRMList(const std::vector<FRMRotamerGroupPtr>& rotamer_groups,
-                                  const std::vector<geom::Transform>& rt_operators){
+GraphPtr Graph::CreateFromFRMList(
+                const std::vector<FRMRotamerGroupPtr>& rotamer_groups,
+                const std::vector<geom::Transform>& rt_operators) {
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "Graph::CreateFromFRMList", 2);
 
   GraphPtr graph(new Graph);
 
@@ -589,7 +598,10 @@ void Graph::Reset(){
   }
 }
 
-std::vector<int> Graph::Solve(uint64_t max_complexity, Real initial_epsilon){
+std::vector<int> Graph::Solve(uint64_t max_complexity, Real initial_epsilon) {
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "Graph::Solve", 2);
 
   TreePtr t;
   bool first_iteration = true;
diff --git a/sidechain/src/rotamer.cc b/sidechain/src/rotamer.cc
index f2b0691bcf1fda91d7663d516c14db6811eb4442..e876fdfa53813595302b168f06743c435d962d1e 100644
--- a/sidechain/src/rotamer.cc
+++ b/sidechain/src/rotamer.cc
@@ -1,4 +1,5 @@
 #include <promod3/sidechain/rotamer.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace promod3{ namespace sidechain{
 
@@ -32,8 +33,12 @@ RRMRotamer::~RRMRotamer(){
   delete [] particles_;
 }
 
-void RRMRotamer::ApplyOnResidue(ost::mol::ResidueHandle& res, bool consider_hydrogens,
-                                const String& new_res_name) const{
+void RRMRotamer::ApplyOnResidue(ost::mol::ResidueHandle& res,
+                                bool consider_hydrogens,
+                                const String& new_res_name) const {
+  
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "RRMRotamer::ApplyOnResidue", 2);
 
   ost::mol::XCSEditor ed = res.GetEntity().EditXCS(ost::mol::BUFFERED_EDIT);
   std::vector<unsigned long> backbone_atoms;
@@ -146,9 +151,13 @@ void FRMRotamer::AddSubrotamerDefinition(const std::vector<int>& definition){
   frame_energies_.push_back(0.0);
 }
 
-void FRMRotamer::ApplyOnResidue(ost::mol::ResidueHandle& res, bool consider_hydrogens,
+void FRMRotamer::ApplyOnResidue(ost::mol::ResidueHandle& res,
+                                bool consider_hydrogens,
                                 const String& new_res_name) const {
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "FRMRotamer::ApplyOnResidue", 2);
+
   if(subrotamer_definitions_.empty()){
     throw promod3::Error("At least one subrotamer must be set in FRM rotamer!");
   }
diff --git a/sidechain/src/rotamer_constructor.cc b/sidechain/src/rotamer_constructor.cc
index 7b49477cbd280a9a88f88f168011dea7b4a75b7f..902f4c0c7f9caf10c499efe3fbcfe3b327490fff 100644
--- a/sidechain/src/rotamer_constructor.cc
+++ b/sidechain/src/rotamer_constructor.cc
@@ -1,5 +1,5 @@
 #include <promod3/sidechain/rotamer_constructor.hh>
-
+#include <promod3/core/runtime_profiling.hh>
 
 using namespace promod3::sidechain;
 
@@ -7383,6 +7383,9 @@ RRMRotamerGroupPtr RRMGroupConstructor(const geom::Vec3& n_pos, const geom::Vec3
                                        const geom::Vec3& cb_pos, RotamerID id,
                                        uint residue_index, RotamerSettingsPtr settings,
                                        std::pair<RotamerLibEntry*,uint>& lib_entries){
+  promod3::core::ScopedTimerPtr prof = 
+    promod3::core::StaticRuntimeProfiler::StartScoped(
+        "sidechain::RRMGroupConstructor", 2);
 
   std::vector<RRMRotamerPtr> rotamers;
   Real chi1,chi2,chi3,chi4, probability;
@@ -7477,6 +7480,9 @@ FRMRotamerGroupPtr FRMGroupConstructor(const geom::Vec3& n_pos, const geom::Vec3
                                        const geom::Vec3& cb_pos, RotamerID id,
                                        uint residue_index, RotamerSettingsPtr settings,
                                        std::pair<RotamerLibEntry*,uint>& lib_entries){
+  promod3::core::ScopedTimerPtr prof = 
+    promod3::core::StaticRuntimeProfiler::StartScoped(
+        "sidechain::FRMGroupConstructor", 2);
 
   std::vector<FRMRotamerPtr> rotamers;
 
@@ -8078,9 +8084,11 @@ FRMRotamerPtr ConstructFRMRotamer(const ost::mol::ResidueHandle& res, RotamerID
                              chi1,sig1,chi2,sig2,chi3,sig3,chi4,sig4);
 }
 
-RRMRotamerGroupPtr ConstructRRMRotamerGroup(const geom::Vec3& n_pos, const geom::Vec3& ca_pos, 
-                                            const geom::Vec3& cb_pos, RotamerID id,
-                                            uint residue_index, BBDepRotamerLibPtr rot_lib, 
+RRMRotamerGroupPtr ConstructRRMRotamerGroup(const geom::Vec3& n_pos,
+                                            const geom::Vec3& ca_pos,
+                                            const geom::Vec3& cb_pos,
+                                            RotamerID id, uint residue_index,
+                                            BBDepRotamerLibPtr rot_lib, 
                                             RotamerSettingsPtr settings,
                                             Real phi, Real psi){
 
diff --git a/sidechain/src/rotamer_group.cc b/sidechain/src/rotamer_group.cc
index 330f7699e972830e89a12f90eae1169d9d1fcdd4..d8f890f43dbef6040e72f67fc858761bfd05257a 100644
--- a/sidechain/src/rotamer_group.cc
+++ b/sidechain/src/rotamer_group.cc
@@ -1,5 +1,5 @@
 #include <promod3/sidechain/rotamer_group.hh>
-
+#include <promod3/core/runtime_profiling.hh>
 
 namespace{
 
@@ -66,6 +66,9 @@ RRMRotamerGroupPtr RRMRotamerGroup::GetTransformedCopy(const geom::Transform& t)
 
 Real* RRMRotamerGroup::CalculatePairwiseEnergies(RRMRotamerGroupPtr other, Real threshold){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                               "RRMRotamerGroup::CalculatePairwiseEnergies", 2);
+
   if(!this->Overlap(other.get())) return NULL;
   std::vector<std::pair<int,int> > overlapping_particles;
   overlapping_particles.reserve(4000);
@@ -99,6 +102,8 @@ Real* RRMRotamerGroup::CalculatePairwiseEnergies(RRMRotamerGroupPtr other, Real
 }
 
 void RRMRotamerGroup::CalculateInternalEnergies(){
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                               "RRMRotamerGroup::CalculateInternalEnergies", 2);
   Real max_p = this->GetMaxP();
   for(iterator i = rotamers_.begin();i != this->end(); ++i){
     (*i)->CalculateInternalEnergy(max_p);
@@ -287,6 +292,8 @@ void FRMRotamerGroup::Merge(const FRMRotamerGroupPtr other){
 
 Real* FRMRotamerGroup::CalculatePairwiseEnergies(FRMRotamerGroupPtr other, Real threshold){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                               "FRMRotamerGroup::CalculatePairwiseEnergies", 2);
 
   if(!this->Overlap(other.get())) return NULL;
 
@@ -447,6 +454,8 @@ Real* FRMRotamerGroup::CalculatePairwiseEnergies(FRMRotamerGroupPtr other, Real
 }
 
 void FRMRotamerGroup::CalculateInternalEnergies(){
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                               "FRMRotamerGroup::CalculateInternalEnergies", 2);
   Real max_p = this->GetMaxP();
   for(iterator i = rotamers_.begin();i != this->end(); ++i){
     (*i)->CalculateInternalEnergy(max_p);
diff --git a/sidechain/src/rotamer_id.cc b/sidechain/src/rotamer_id.cc
index d3835ec11e2ede957fe7d67309c267d0286183b4..8c9807f7e7ae2b2ced62d11321722a598a2450d9 100644
--- a/sidechain/src/rotamer_id.cc
+++ b/sidechain/src/rotamer_id.cc
@@ -1,12 +1,13 @@
 #include <promod3/sidechain/rotamer_id.hh>
-
-
-
+#include <promod3/core/runtime_profiling.hh>
 
 namespace promod3{ namespace sidechain{
 
 RotamerID TLCToRotID(const String& tlc){
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "sidechain::TLCToRotID", 2);
+
   switch(tlc[0]){
 
     case 'A':{
diff --git a/sidechain/src/rotamer_lib.cc b/sidechain/src/rotamer_lib.cc
index 4e2a567ac74752062e3bde01e6e4e650e3fd6159..4f06f77fc1f36747f26844f080a222fbc828ed9a 100644
--- a/sidechain/src/rotamer_lib.cc
+++ b/sidechain/src/rotamer_lib.cc
@@ -1,5 +1,6 @@
 #include <promod3/sidechain/rotamer_lib.hh>
 #include <promod3/core/check_io.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace{
 
@@ -199,6 +200,9 @@ void RotamerLib::AddRotamer(RotamerID id, const RotamerLibEntry& entry){
 
 std::pair<RotamerLibEntry*,uint> RotamerLib::QueryLib(RotamerID id) const{
 
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "RotamerLib::QueryLib", 2);
+
   if(!readonly_){
     throw promod3::Error("Can only read rotamers from static library!");
   }
diff --git a/sidechain/src/sidechain_connector.cc b/sidechain/src/sidechain_connector.cc
index 4c5a295ae9015b272003720998824246464e0237..c8e5c21966e75605c268289514016fbbc1beafae 100644
--- a/sidechain/src/sidechain_connector.cc
+++ b/sidechain/src/sidechain_connector.cc
@@ -1,5 +1,5 @@
 #include <promod3/sidechain/sidechain_connector.hh>
-
+#include <promod3/core/runtime_profiling.hh>
 
 namespace{
 
@@ -340,7 +340,10 @@ void ConnectALA(ost::mol::ResidueHandle& res, ost::mol::XCSEditor& ed){
 
 namespace promod3{ namespace sidechain{
 
-void ConnectSidechain(ost::mol::ResidueHandle& res, RotamerID id){
+void ConnectSidechain(ost::mol::ResidueHandle& res, RotamerID id) {
+
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "sidechain::ConnectSidechain", 2);
 
   ost::mol::XCSEditor ed = res.GetEntity().EditXCS(ost::mol::BUFFERED_EDIT);
 
diff --git a/sidechain/src/sidechain_object_loader.cc b/sidechain/src/sidechain_object_loader.cc
index 21936a138cb3231be23272d8ea14909a93c824cb..9480ad2bc87050cef37b4f1c505c3c446d7c2881 100644
--- a/sidechain/src/sidechain_object_loader.cc
+++ b/sidechain/src/sidechain_object_loader.cc
@@ -1,15 +1,20 @@
 #include <promod3/sidechain/sidechain_object_loader.hh>
 #include <promod3/config.hh>
+#include <promod3/core/runtime_profiling.hh>
 
 namespace promod3{ namespace sidechain{
 
 BBDepRotamerLibPtr LoadDunbrackLib(){
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "sidechain::LoadDunbrackLib", 2);
   String path = GetProMod3BinaryPath("sidechain_data", "2010DunbrackLib.dat");
   BBDepRotamerLibPtr p = BBDepRotamerLib::Load(path);
   return p;  
 }
 
 RotamerLibPtr LoadPenultimateLib(){
+  core::ScopedTimerPtr prof = core::StaticRuntimeProfiler::StartScoped(
+                                "sidechain::LoadPenultimateLib", 2);
   String path = GetProMod3BinaryPath("sidechain_data", "PenultimateLib.dat");
   RotamerLibPtr p = RotamerLib::Load(path);
   return p;