Skip to content
Snippets Groups Projects
Forked from waLBerla / waLBerla
606 commits behind the upstream repository.
TimingTree.h 8.37 KiB
//======================================================================================================================
//
//  This file is part of waLBerla. waLBerla is free software: you can
//  redistribute it and/or modify it under the terms of the GNU General Public
//  License as published by the Free Software Foundation, either version 3 of
//  the License, or (at your option) any later version.
//
//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
//  for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
//
//! \file TimingTree.h
//! \ingroup core
//! \author Sebastian Eibl <sebastian.eibl@fau.de>
//! \author Martin Bauer <martin.bauer@fau.de>
//
//======================================================================================================================

#pragma once

#include "TimingNode.h"

#include "core/debug/Debug.h"
#include "core/logging/Logging.h"
#include "core/mpi/MPIManager.h"
#include "core/mpi/Reduce.h"

#include <algorithm>
#include <iostream>
#include <map>
#include <string>


namespace walberla {
namespace timing {

/***********************************************************************************************************************
 * \brief Hierarchical structure of timers.
 *
 * Timer class to time different code snippets. Nested timers can be created and will be outputed
 * in a tree like structure. Also works MPI parallel by using the reduce function but is NOT
 * threadsafe!
 *
 * \ingroup timing
 *
 **********************************************************************************************************************/
template< typename TP >  // Timing policy
class TimingTree
{
public:
   /// Creates and initialises the timing structure
   TimingTree();
   TimingTree(const TimingTree& tt);
   TimingTree<TP>& operator=(const TimingTree<TP>& tt);

   void swap(TimingTree<TP>& tt);

   /// Starts a timer beneath the current hierarchy level
   void start(const std::string& name);
   /// Stops the last started timer and jumps back one hierarchy level
   void stop(const std::string& name);
   /// Checks if specified timer is currently running.
   bool isTimerRunning(const std::string& name) const;
   /// Resets the the timing hierarchy
   void reset();
   /// Collects all the timing data from different processes
   ///
   /// \param rt type of reduction used
   /// \param targetRank
   /// \param callSynchronize call synchronize() before doing the reduction
   /// \return
   ///
   /// \attention Setting callSynchronize to false can lead to deadlocks during reduction if different ranks
   ///            have different timing nodes! Setting it to true causes an additional communication.
   TimingTree<TP> getReduced( ReduceType rt = REDUCE_TOTAL, int targetRank = 0, bool callSynchronize = true ) const;

   /// Adds entries which only exist on other processes. Has to be collectively called on all processes!
   void synchronize();

   /// Returns the raw tree data structure
   const TimingNode<TP>& getRawData() const;

   const Timer<TP>& operator[](const std::string& name) const;
   inline bool timerExists ( const std::string & n ) const;

   /// Returns the name of the currently running timer
   /// Might be expensive due to value search.
   std::string getCurrentTimerName() const;

   /// Returns a copy of the timing tree containing the remaining time as a subnode
   TimingTree< TP > getCopyWithRemainder() const;

private:
   /// Tree data structure
   TimingNode<TP>  root_;
   /// Pointer to the current hierarchy level.
   TimingNode<TP>* current_;
};

/// \relates TimingTree
template< typename TP >  // Timing policy
std::ostream& operator<<( std::ostream& os, const TimingTree<TP>& tt )
{
   os << tt.getRawData();
   return os;
}

template< typename TP >  // Timing policy
TimingTree<TP>::TimingTree()
      : current_(&root_)
{

}

template< typename TP >  // Timing policy
TimingTree<TP>::TimingTree(const TimingTree<TP>& tt)
      : root_(tt.root_)
      , current_(&root_)
{
   WALBERLA_ASSERT_EQUAL(tt.current_, &tt.root_, "Copying is only allowed for stopped TimingTrees!\nTimer still running: " << getCurrentTimerName() );
}

template< typename TP >  // Timing policy
TimingTree<TP>& TimingTree<TP>::operator=(const TimingTree<TP>& tt)
{
   TimingTree<TP> tmp (tt);
   tmp.swap(*this);
   return *this;
}

template< typename TP >  // Timing policy
void TimingTree<TP>::swap(TimingTree<TP>& tt)
{
   std::swap(current_, tt.current_);
   std::swap(root_, tt.root_);
}

/// \param name timer name. '.' is not allowed in the timer name!
template< typename TP >  // Timing policy
void TimingTree<TP>::start(const std::string& name)
{
   if (name.find_first_of(".") != std::string::npos)
   {
      WALBERLA_LOG_WARNING("'.' not allowed in timer name!");
   }
   auto tmp = current_;
   current_ = &(current_->tree_[name]);
   current_->last_ = tmp; //if node is created by previous call last_ is not set
   current_->timer_.start();
//   WALBERLA_LOG_DEVEL("Timer started: " << name);
}

template< typename TP >  // Timing policy
void TimingTree<TP>::stop(const std::string& name)
{
   if (name.find_first_of(".") != std::string::npos)
   {
      WALBERLA_LOG_WARNING("'.' not allowed in timer name!");
   }
   WALBERLA_ASSERT_NOT_NULLPTR( current_->last_ );
   auto timerIt = current_->last_->tree_.find(name);
   if ((timerIt == current_->last_->tree_.end()) || (&(timerIt->second) != current_))
   {
      WALBERLA_LOG_WARNING("Trying to stop timer which is not running: " << name << "\nCurrently Running: " << getCurrentTimerName() );
      const auto nan = std::numeric_limits<double>::quiet_NaN();
      current_->tree_[name].timer_ = Timer<TP>(1, nan, nan, nan, nan);
   } else
   {
      current_->timer_.end();
      current_ = current_->last_;
//      WALBERLA_LOG_DEVEL("Timer stoped: " << name);
   }
}

template< typename TP >  // Timing policy
bool TimingTree<TP>::isTimerRunning(const std::string& name) const
{
   WALBERLA_ASSERT_NOT_NULLPTR( current_->last_ );
   auto timerIt = current_->last_->tree_.find(name);
   return !((timerIt == current_->last_->tree_.end()) || (&(timerIt->second) != current_));
}

template< typename TP >  // Timing policy
void TimingTree<TP>::reset()
{
   walberla::timing::reset(root_);
}

template< typename TP >  // Timing policy
TimingTree<TP> TimingTree<TP>::getReduced( ReduceType rt, int targetRank, bool callSynchronize ) const
{
   TimingTree<TP> tt(*this);
   if (callSynchronize)
   {
      tt.synchronize();
   }
   reduceInplace( tt.root_, rt, targetRank );
   return tt;
}

template< typename TP >  // Timing policy
void TimingTree<TP>::synchronize()
{
   synchronizeEntries( root_ );
}

template< typename TP >  // Timing policy
const TimingNode<TP>& TimingTree<TP>::getRawData() const
{
   return root_;
}

/// Finds the specified timer in the timing hierarchy
/// \param name timer name which may include more than one hierarchy separated by "."
/// \code tt["firstLevel.secondLevel.thirdLevel.timerName"].total(); \endcode
template< typename TP >  // Timing policy
const Timer<TP>& TimingTree<TP>::operator[](const std::string& name) const
{
   return findTimer(root_, name);
}

/// Checks if the specified timer exists in the timing hierarchy
/// \param name timer name which may include more than one hierarchy separated by "."
/// \code tt.timerExists("firstLevel.secondLevel.thirdLevel.timerName"); \endcode
template< typename TP >  // Timing policy
bool TimingTree<TP>::timerExists(const std::string& name) const
{
   return walberla::timing::timerExists(root_, name);
}

template< typename TP >  // Timing policy
std::string TimingTree<TP>::getCurrentTimerName() const
{
   if (current_ == current_->last_)
   {
      return "No timer running!";
   }
   for (auto it = current_->last_->tree_.begin(); it != current_->last_->tree_.end(); ++it)
   {
      if (&(it->second) == current_)
         return it->first;
   }
   return "No timer found!";
}

template < typename TP > // Timing policy
TimingTree< TP > TimingTree< TP >::getCopyWithRemainder() const
{
   TimingTree< TP > tt( *this );
   timing::internal::addRemainderNodes< TP >( tt.root_ );
   return tt;
}

}

using WcTimingTree = timing::TimingTree<timing::WcPolicy>;
using CpuTimingTree = timing::TimingTree<timing::CpuPolicy>;
}