Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v36r16 (ea80daf8)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
CPUCruncher.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 #include "CPUCruncher.h"
13 #include "HiveNumbers.h"
14 #include <ctime>
15 #include <sys/resource.h>
16 #include <sys/times.h>
17 
18 #include <tbb/blocked_range.h>
19 #include <tbb/parallel_for.h>
20 #include <tbb/tick_count.h>
21 #include <thread>
22 
24 
26 
27 #define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
28 #define DEBUG_MSG ON_DEBUG debug()
29 
30 #define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
31 #define VERBOSE_MSG ON_VERBOSE verbose()
32 
33 //------------------------------------------------------------------------------
34 
35 CPUCruncher::CPUCruncher( const std::string& name, // the algorithm instance name
36  ISvcLocator* pSvc )
37  : GaudiAlgorithm( name, pSvc ) {
38 
39  // Register the algo in the static concurrent hash map in order to
40  // monitor the # of copies
41  CHM::accessor name_ninstances;
42  m_name_ncopies_map.insert( name_ninstances, name );
43  name_ninstances->second += 1;
44 }
45 
47  for ( uint i = 0; i < m_inputHandles.size(); ++i ) delete m_inputHandles[i];
48 
49  for ( uint i = 0; i < m_outputHandles.size(); ++i ) delete m_outputHandles[i];
50 }
51 
53  auto sc = GaudiAlgorithm::initialize();
54  if ( !sc ) return sc;
55 
56  m_crunchSvc = serviceLocator()->service( "CPUCrunchSvc" );
57  if ( !m_crunchSvc.isValid() ) {
58  fatal() << "unable to acquire CPUCruncSvc" << endmsg;
59  return StatusCode::FAILURE;
60  }
61 
62  // if an algorithm was setup to sleep, for whatever period, it effectively models CPU-blocking behavior
63  if ( m_sleepFraction != 0.0f ) setBlocking( true );
64 
65  // This is a bit ugly. There is no way to declare a vector of DataObjectHandles, so
66  // we need to wait until initialize when we've read in the input and output key
67  // properties, and know their size, and then turn them
68  // into Handles and register them with the framework by calling declareProperty. We
69  // could call declareInput/declareOutput on them too.
70 
71  int i = 0;
72  for ( auto k : m_inpKeys ) {
73  DEBUG_MSG << "adding input key " << k << endmsg;
75  declareProperty( "dummy_in_" + std::to_string( i ), *( m_inputHandles.back() ) );
76  i++;
77  }
78 
79  i = 0;
80  for ( auto k : m_outKeys ) {
81  DEBUG_MSG << "adding output key " << k << endmsg;
83  declareProperty( "dummy_out_" + std::to_string( i ), *( m_outputHandles.back() ) );
84  i++;
85  }
86 
87  return sc;
88 }
89 
90 //------------------------------------------------------------------------------
92  //
93  for ( const auto& k : outputDataObjs() ) {
94  auto outputHandle = new DataObjectHandle<DataObject>( k, Gaudi::DataHandle::Writer, this );
95  VERBOSE_MSG << "found late-attributed output: " << outputHandle->objKey() << endmsg;
96  m_outputHandles.push_back( outputHandle );
97  declareProperty( "dummy_out_" + outputHandle->objKey(), *( m_outputHandles.back() ) );
98  }
99 
101 
102  m_declAugmented = true;
103 }
104 
105 //------------------------------------------------------------------------------
106 
107 StatusCode CPUCruncher::execute() // the execution of the algorithm
108 {
109 
111 
112  float crunchtime;
113 
114  if ( m_local_rndm_gen ) {
115  /* This will disappear with a thread safe random number generator service.
116  * Use basic Box-Muller to generate Gaussian random numbers.
117  * The quality is not good for in depth study given that the generator is a
118  * linear congruent.
119  * Throw away basically a free number: we are in a cpu cruncher after all.
120  * The seed is taken from the clock, but we could assign a seed per module to
121  * ensure reproducibility.
122  *
123  * This is not an overkill but rather an exercise towards a thread safe
124  * random number generation.
125  */
126 
127  auto getGausRandom = []( double mean, double sigma ) -> double {
128  unsigned int seed = std::clock();
129 
130  auto getUnifRandom = []( unsigned int& seed ) -> double {
131  // from "Numerical Recipes"
132  constexpr unsigned int m = 232;
133  constexpr unsigned int a = 1664525;
134  constexpr unsigned int c = 1013904223;
135  seed = ( a * seed + c ) % m;
136  const double unif = double( seed ) / m;
137  return unif;
138  };
139 
140  double unif1, unif2;
141  do {
142  unif1 = getUnifRandom( seed );
143  unif2 = getUnifRandom( seed );
144  } while ( unif1 == 0. );
145 
146  const double normal = sqrt( -2. * log( unif1 ) ) * cos( 2 * M_PI * unif2 );
147 
148  return normal * sigma + mean;
149  };
150 
151  crunchtime = fabs( getGausRandom( m_avg_runtime * ( 1. - m_sleepFraction ), m_var_runtime ) );
152  // End Of temp block
153  } else {
154  // Should be a member.
156  crunchtime = std::fabs( rndmgaus() );
157  }
158  unsigned int crunchtime_ms = 1000 * crunchtime;
159 
160  // Prepare to sleep (even if we won't enter the following if clause for sleeping).
161  // This is needed to distribute evenly among all algorithms the overhead (around sleeping) which is harmful when
162  // trying to achieve uniform distribution of algorithm timings.
163  const double dreamtime = m_avg_runtime * m_sleepFraction;
164  const std::chrono::duration<double> dreamtime_duration( dreamtime );
165  tbb::tick_count startSleeptbb;
166  tbb::tick_count endSleeptbb;
167 
168  // Start to measure the total time here, together with the dreaming process straight ahead
169  tbb::tick_count starttbb = tbb::tick_count::now();
170 
171  // If the algorithm was declared as CPU-blocking, we will replace requested part of crunching with plain sleeping
172  if ( isBlocking() ) {
173  // in this block (and not in other places around) msgLevel is checked for the same reason as above, when
174  // preparing to sleep several lines above: to reduce as much as possible the overhead around sleeping
175  DEBUG_MSG << "Dreaming time will be: " << int( 1000 * dreamtime ) << " ms" << endmsg;
176 
177  ON_DEBUG startSleeptbb = tbb::tick_count::now();
178  std::this_thread::sleep_for( dreamtime_duration );
179  ON_DEBUG endSleeptbb = tbb::tick_count::now();
180 
181  // actual sleeping time can be longer due to scheduling or resource contention delays
182  ON_DEBUG {
183  const double actualDreamTime = ( endSleeptbb - startSleeptbb ).seconds();
184  debug() << "Actual dreaming time was: " << int( 1000 * actualDreamTime ) << "ms" << endmsg;
185  }
186  } // end of "sleeping block"
187 
188  DEBUG_MSG << "Crunching time will be: " << crunchtime_ms << " ms" << endmsg;
190  DEBUG_MSG << "Start event " << context.evt() << " in slot " << context.slot() << " on pthreadID " << std::hex
191  << pthread_self() << std::dec << endmsg;
192 
193  VERBOSE_MSG << "inputs number: " << m_inputHandles.size() << endmsg;
194  for ( auto& inputHandle : m_inputHandles ) {
195  if ( !inputHandle->isValid() ) continue;
196 
197  VERBOSE_MSG << "get from TS: " << inputHandle->objKey() << endmsg;
198  DataObject* obj = nullptr;
199  for ( unsigned int i = 0; i < m_rwRepetitions; ++i ) { obj = inputHandle->get(); }
200  if ( obj == nullptr ) error() << "A read object was a null pointer." << endmsg;
201  }
202 
203  if ( m_nParallel > 1 ) {
204  tbb::parallel_for( tbb::blocked_range<size_t>( 0, m_nParallel ), [&]( tbb::blocked_range<size_t> r ) {
205  m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
206  debug() << "CPUCrunch complete in TBB parallel for block " << r.begin() << " to " << r.end() << endmsg;
207  } );
208  } else {
209  m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
210  }
211 
212  // Return error on fraction of events if configured
213  if ( m_failNEvents > 0 && context.evt() > 0 && ( context.evt() % m_failNEvents ) == 0 ) {
214  return StatusCode::FAILURE;
215  }
216 
217  VERBOSE_MSG << "outputs number: " << m_outputHandles.size() << endmsg;
218  for ( auto& outputHandle : m_outputHandles ) {
219  if ( !outputHandle->isValid() ) continue;
220 
221  VERBOSE_MSG << "put to TS: " << outputHandle->objKey() << endmsg;
222  outputHandle->put( std::make_unique<DataObject>() );
223  }
224 
225  tbb::tick_count endtbb = tbb::tick_count::now();
226  const double actualRuntime = ( endtbb - starttbb ).seconds();
227 
228  DEBUG_MSG << "Finish event " << context.evt() << " in " << int( 1000 * actualRuntime ) << " ms" << endmsg;
229 
230  DEBUG_MSG << "Timing: ExpectedCrunchtime= " << crunchtime_ms << " ms. ExpectedDreamtime= " << int( 1000 * dreamtime )
231  << " ms. ActualTotalRuntime= " << int( 1000 * actualRuntime )
232  << " ms. Ratio= " << ( crunchtime + dreamtime ) / actualRuntime << endmsg;
233 
235 
236  return StatusCode::SUCCESS;
237 }
238 
239 //------------------------------------------------------------------------------
240 
241 StatusCode CPUCruncher::finalize() // the finalization of the algorithm
242 {
243  MsgStream log( msgSvc(), name() );
244 
245  unsigned int ninstances;
246 
247  {
248  CHM::const_accessor const_name_ninstances;
249  m_name_ncopies_map.find( const_name_ninstances, name() );
250  ninstances = const_name_ninstances->second;
251  }
252 
253  constexpr double s2ms = 1000.;
254  // do not show repetitions
255  if ( ninstances != 0 ) {
256  info() << "Summary: name= " << name() << "\t avg_runtime= " << m_avg_runtime * s2ms << "\t n_clones= " << ninstances
257  << endmsg;
258 
259  CHM::accessor name_ninstances;
260  m_name_ncopies_map.find( name_ninstances, name() );
261  name_ninstances->second = 0;
262  }
263 
264  return GaudiAlgorithm::finalize();
265 }
266 
267 //------------------------------------------------------------------------------
CPUCruncher::m_inpKeys
Gaudi::Property< std::vector< std::string > > m_inpKeys
Definition: CPUCruncher.h:65
Gaudi::Accumulators::sqrt
auto sqrt(std::chrono::duration< Rep, Period > d)
sqrt for std::chrono::duration
Definition: Counters.h:34
CPUCruncher::m_rwRepetitions
Gaudi::Property< unsigned int > m_rwRepetitions
Definition: CPUCruncher.h:72
std::this_thread::sleep_for
T sleep_for(T... args)
CPUCruncher
Definition: CPUCruncher.h:29
CPUCruncher::m_loader
Gaudi::Property< bool > m_loader
Definition: CPUCruncher.h:63
std::string
STL class.
GaudiAlgorithm::finalize
StatusCode finalize() override
standard finalization method
Definition: GaudiAlgorithm.cpp:65
Gaudi.Configuration.log
log
Definition: Configuration.py:30
Gaudi::Algorithm::randSvc
SmartIF< IRndmGenSvc > & randSvc() const
The standard RandomGen service, Return a pointer to the service if present.
Definition: Algorithm.cpp:565
std::fabs
T fabs(T... args)
CPUCruncher::initialize
StatusCode initialize() override
Its initialization.
Definition: CPUCruncher.cpp:52
Gaudi::Hive::currentContext
GAUDI_API const EventContext & currentContext()
Definition: ThreadLocalContext.cpp:30
Gaudi::Algorithm::isBlocking
bool isBlocking() const
Definition: Algorithm.h:394
Gaudi::Algorithm::name
const std::string & name() const override
The identifying name of the algorithm object.
Definition: Algorithm.cpp:528
Gaudi::details::LegacyAlgorithmAdapter::setFilterPassed
void setFilterPassed(bool state) const
Set the filter passed flag to the specified state.
Definition: LegacyAlgorithm.cpp:30
std::vector::size
T size(T... args)
CPUCruncher::declareRuntimeRequestedOutputs
void declareRuntimeRequestedOutputs()
The CPU intensive function.
Definition: CPUCruncher.cpp:91
ISvcLocator
Definition: ISvcLocator.h:46
std::chrono::duration
Gaudi::Algorithm::declareProperty
Gaudi::Details::PropertyBase * declareProperty(const std::string &name, ToolHandle< T > &hndl, const std::string &doc="none")
Definition: Algorithm.h:302
Gaudi::Algorithm::serviceLocator
SmartIF< ISvcLocator > & serviceLocator() const override
The standard service locator.
Definition: Algorithm.cpp:572
CPUCruncher::m_invertCFD
Gaudi::Property< bool > m_invertCFD
Definition: CPUCruncher.h:76
gaudirun.c
c
Definition: gaudirun.py:527
std::vector::back
T back(T... args)
CPUCruncher.h
CPUCruncher::m_declAugmented
bool m_declAugmented
Definition: CPUCruncher.h:62
HiveNumbers.h
DataObjectHandle< DataObject >
Gaudi::DataHandle::Writer
@ Writer
Definition: DataHandle.h:40
CPUCruncher::m_var_runtime
Gaudi::Property< double > m_var_runtime
Definition: CPUCruncher.h:69
std::hex
T hex(T... args)
std::vector::push_back
T push_back(T... args)
CPUCruncher::CPUCruncher
CPUCruncher()
the default constructor is disabled
std::clock
T clock(T... args)
CPUCruncher::m_failNEvents
Gaudi::Property< unsigned int > m_failNEvents
Definition: CPUCruncher.h:77
SmartIF::isValid
bool isValid() const
Allow for check if smart pointer is valid.
Definition: SmartIF.h:72
CPUCruncher::m_outKeys
Gaudi::Property< std::vector< std::string > > m_outKeys
Definition: CPUCruncher.h:66
TimingHistograms.name
name
Definition: TimingHistograms.py:25
DataHandleHolderBase< PropertyHolder< CommonMessaging< implements< IAlgorithm, IDataHandleHolder, IProperty, IStateful > > > >::initDataHandleHolder
void initDataHandleHolder()
initializes all handles - called by the sysInitialize method of any descendant of this
Definition: DataHandleHolderBase.h:94
StatusCode
Definition: StatusCode.h:65
Rndm::Gauss
Parameters for the Gauss random number generation.
Definition: RndmGenerators.h:32
HistoDumpEx.r
r
Definition: HistoDumpEx.py:20
Gaudi::Units::m
constexpr double m
Definition: SystemOfUnits.h:108
GaudiAlgorithm
Definition: GaudiAlgorithm.h:104
GaudiAlgorithm::initialize
StatusCode initialize() override
standard initialization method
Definition: GaudiAlgorithm.cpp:52
CPUCruncher::execute
StatusCode execute() override
the execution of the algorithm
Definition: CPUCruncher.cpp:107
Gaudi::Algorithm::setBlocking
void setBlocking(bool value)
Definition: Algorithm.h:396
std::to_string
T to_string(T... args)
CPUCruncher::finalize
StatusCode finalize() override
the finalization of the algorithm
Definition: CPUCruncher.cpp:241
CPUCruncher::m_local_rndm_gen
Gaudi::Property< bool > m_local_rndm_gen
Definition: CPUCruncher.h:70
CPUCruncher::CHM
tbb::concurrent_hash_map< std::string, unsigned int > CHM
Definition: CPUCruncher.h:32
endmsg
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:203
GaudiPython.HistoUtils.mean
mean
Definition: HistoUtils.py:904
MsgStream
Definition: MsgStream.h:34
GaudiCommon< Algorithm >::context
const std::string & context() const
Returns the "context" string. Used to identify different processing states.
Definition: GaudiCommon.h:676
CPUCruncher::m_inputHandles
std::vector< DataObjectHandle< DataObject > * > m_inputHandles
Definition: CPUCruncher.h:84
ON_DEBUG
#define ON_DEBUG
Definition: CPUCruncher.cpp:27
CPUCruncher::m_name_ncopies_map
static CHM m_name_ncopies_map
Definition: CPUCruncher.h:87
ThreadLocalContext.h
StatusCode::SUCCESS
constexpr static const auto SUCCESS
Definition: StatusCode.h:100
DEBUG_MSG
#define DEBUG_MSG
Definition: CPUCruncher.cpp:28
CPUCruncher::m_avg_runtime
Gaudi::Property< double > m_avg_runtime
Definition: CPUCruncher.h:68
DataHandleHolderBase< PropertyHolder< CommonMessaging< implements< IAlgorithm, IDataHandleHolder, IProperty, IStateful > > > >::outputDataObjs
const DataObjIDColl & outputDataObjs() const override
Definition: DataHandleHolderBase.h:84
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition: PluginServiceV1.h:46
CPUCruncher::m_sleepFraction
Gaudi::Property< float > m_sleepFraction
Definition: CPUCruncher.h:73
OffloadAtlasMCRecoScenario.seed
seed
Definition: OffloadAtlasMCRecoScenario.py:52
EventContext
Definition: EventContext.h:34
DataObject
Definition: DataObject.h:40
CPUCruncher::~CPUCruncher
virtual ~CPUCruncher()
virtual & protected desctrustor
Definition: CPUCruncher.cpp:46
Gaudi::DataHandle::Reader
@ Reader
Definition: DataHandle.h:40
CPUCruncher::m_crunchSvc
SmartIF< ICPUCrunchSvc > m_crunchSvc
Definition: CPUCruncher.h:90
AsyncIncidents.msgSvc
msgSvc
Definition: AsyncIncidents.py:34
StatusCode::FAILURE
constexpr static const auto FAILURE
Definition: StatusCode.h:101
VERBOSE_MSG
#define VERBOSE_MSG
Definition: CPUCruncher.cpp:31
CPUCruncher::m_outputHandles
std::vector< DataObjectHandle< DataObject > * > m_outputHandles
Definition: CPUCruncher.h:85
HiveRndm::HiveNumbers
Definition: HiveNumbers.h:38
CPUCruncher::m_nParallel
Gaudi::Property< int > m_nParallel
Definition: CPUCruncher.h:78