-
Sebastian Eibl authored
This prevents possible deadlocks during reduction at the cost of an additional communication.
6a8bb6a0
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>;
}