Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v31r0 (aeb156f0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
CPUCruncher.cpp
Go to the documentation of this file.
1 #include "CPUCruncher.h"
3 #include "HiveNumbers.h"
4 #include <ctime>
5 #include <sys/resource.h>
6 #include <sys/times.h>
7 
8 #include <tbb/tick_count.h>
9 #include <thread>
10 
12 
14 
15 #define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
16 #define DEBUG_MSG ON_DEBUG debug()
17 
18 #define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
19 #define VERBOSE_MSG ON_VERBOSE verbose()
20 
21 //------------------------------------------------------------------------------
22 
23 CPUCruncher::CPUCruncher( const std::string& name, // the algorithm instance name
24  ISvcLocator* pSvc )
25  : GaudiAlgorithm( name, pSvc ) {
26 
27  // Register the algo in the static concurrent hash map in order to
28  // monitor the # of copies
29  CHM::accessor name_ninstances;
30  m_name_ncopies_map.insert( name_ninstances, name );
31  name_ninstances->second += 1;
32 }
33 
35  for ( uint i = 0; i < m_inputHandles.size(); ++i ) delete m_inputHandles[i];
36 
37  for ( uint i = 0; i < m_outputHandles.size(); ++i ) delete m_outputHandles[i];
38 }
39 
41  auto sc = GaudiAlgorithm::initialize();
42  if ( !sc ) return sc;
43 
44  m_crunchSvc = serviceLocator()->service( "CPUCrunchSvc" );
45  if ( !m_crunchSvc.isValid() ) {
46  fatal() << "unable to acquire CPUCruncSvc" << endmsg;
47  return StatusCode::FAILURE;
48  }
49 
50  // if an algorithm was setup to sleep, for whatever period, it effectively becomes I/O-bound
51  if ( m_sleepFraction != 0.0f ) setIOBound( true );
52 
53  // This is a bit ugly. There is no way to declare a vector of DataObjectHandles, so
54  // we need to wait until initialize when we've read in the input and output key
55  // properties, and know their size, and then turn them
56  // into Handles and register them with the framework by calling declareProperty. We
57  // could call declareInput/declareOutput on them too.
58 
59  int i = 0;
60  for ( auto k : m_inpKeys ) {
61  DEBUG_MSG << "adding input key " << k << endmsg;
63  declareProperty( "dummy_in_" + std::to_string( i ), *( m_inputHandles.back() ) );
64  i++;
65  }
66 
67  i = 0;
68  for ( auto k : m_outKeys ) {
69  DEBUG_MSG << "adding output key " << k << endmsg;
71  declareProperty( "dummy_out_" + std::to_string( i ), *( m_outputHandles.back() ) );
72  i++;
73  }
74 
75  return sc;
76 }
77 
78 //------------------------------------------------------------------------------
80  //
81  for ( const auto& k : outputDataObjs() ) {
82  auto outputHandle = new DataObjectHandle<DataObject>( k, Gaudi::DataHandle::Writer, this );
83  VERBOSE_MSG << "found late-attributed output: " << outputHandle->objKey() << endmsg;
84  m_outputHandles.push_back( outputHandle );
85  declareProperty( "dummy_out_" + outputHandle->objKey(), *( m_outputHandles.back() ) );
86  }
87 
89 
90  m_declAugmented = true;
91 }
92 
93 //------------------------------------------------------------------------------
94 
95 StatusCode CPUCruncher::execute() // the execution of the algorithm
96 {
97 
99 
100  float crunchtime;
101 
102  if ( m_local_rndm_gen ) {
103  /* This will disappear with a thread safe random number generator service.
104  * Use basic Box-Muller to generate Gaussian random numbers.
105  * The quality is not good for in depth study given that the generator is a
106  * linear congruent.
107  * Throw away basically a free number: we are in a cpu cruncher after all.
108  * The seed is taken from the clock, but we could assign a seed per module to
109  * ensure reproducibility.
110  *
111  * This is not an overkill but rather an exercise towards a thread safe
112  * random number generation.
113  */
114 
115  auto getGausRandom = []( double mean, double sigma ) -> double {
116  unsigned int seed = std::clock();
117 
118  auto getUnifRandom = []( unsigned int& seed ) -> double {
119  // from "Numerical Recipes"
120  constexpr unsigned int m = 232;
121  constexpr unsigned int a = 1664525;
122  constexpr unsigned int c = 1013904223;
123  seed = ( a * seed + c ) % m;
124  const double unif = double( seed ) / m;
125  return unif;
126  };
127 
128  double unif1, unif2;
129  do {
130  unif1 = getUnifRandom( seed );
131  unif2 = getUnifRandom( seed );
132  } while ( unif1 == 0. );
133 
134  const double normal = sqrt( -2. * log( unif1 ) ) * cos( 2 * M_PI * unif2 );
135 
136  return normal * sigma + mean;
137  };
138 
139  crunchtime = fabs( getGausRandom( m_avg_runtime * ( 1. - m_sleepFraction ), m_var_runtime ) );
140  // End Of temp block
141  } else {
142  // Should be a member.
144  crunchtime = std::fabs( rndmgaus() );
145  }
146  unsigned int crunchtime_ms = 1000 * crunchtime;
147 
148  // Prepare to sleep (even if we won't enter the following if clause for sleeping).
149  // This is needed to distribute evenly among all algorithms the overhead (around sleeping) which is harmful when
150  // trying to achieve uniform distribution of algorithm timings.
151  const double dreamtime = m_avg_runtime * m_sleepFraction;
152  const std::chrono::duration<double> dreamtime_duration( dreamtime );
153  tbb::tick_count startSleeptbb;
154  tbb::tick_count endSleeptbb;
155 
156  // Start to measure the total time here, together with the dreaming process straight ahead
157  tbb::tick_count starttbb = tbb::tick_count::now();
158 
159  // If the algorithm was set as I/O-bound, we will replace requested part of crunching with plain sleeping
160  if ( isIOBound() ) {
161  // in this block (and not in other places around) msgLevel is checked for the same reason as above, when
162  // preparing to sleep several lines above: to reduce as much as possible the overhead around sleeping
163  DEBUG_MSG << "Dreaming time will be: " << int( 1000 * dreamtime ) << " ms" << endmsg;
164 
165  ON_DEBUG startSleeptbb = tbb::tick_count::now();
166  std::this_thread::sleep_for( dreamtime_duration );
167  ON_DEBUG endSleeptbb = tbb::tick_count::now();
168 
169  // actual sleeping time can be longer due to scheduling or resource contention delays
170  ON_DEBUG {
171  const double actualDreamTime = ( endSleeptbb - startSleeptbb ).seconds();
172  debug() << "Actual dreaming time was: " << int( 1000 * actualDreamTime ) << "ms" << endmsg;
173  }
174  } // end of "sleeping block"
175 
176  DEBUG_MSG << "Crunching time will be: " << crunchtime_ms << " ms" << endmsg;
178  DEBUG_MSG << "Start event " << context.evt() << " in slot " << context.slot() << " on pthreadID " << std::hex
179  << pthread_self() << std::dec << endmsg;
180 
181  VERBOSE_MSG << "inputs number: " << m_inputHandles.size() << endmsg;
182  for ( auto& inputHandle : m_inputHandles ) {
183  if ( !inputHandle->isValid() ) continue;
184 
185  VERBOSE_MSG << "get from TS: " << inputHandle->objKey() << endmsg;
186  DataObject* obj = nullptr;
187  for ( unsigned int i = 0; i < m_rwRepetitions; ++i ) { obj = inputHandle->get(); }
188  if ( obj == nullptr ) error() << "A read object was a null pointer." << endmsg;
189  }
190 
191  m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
192 
193  // Return error on fraction of events if configured
194  if ( m_failNEvents > 0 && context.evt() > 0 && ( context.evt() % m_failNEvents ) == 0 ) {
195  return StatusCode::FAILURE;
196  }
197 
198  VERBOSE_MSG << "outputs number: " << m_outputHandles.size() << endmsg;
199  for ( auto& outputHandle : m_outputHandles ) {
200  if ( !outputHandle->isValid() ) continue;
201 
202  VERBOSE_MSG << "put to TS: " << outputHandle->objKey() << endmsg;
203  outputHandle->put( new DataObject() );
204  }
205 
206  tbb::tick_count endtbb = tbb::tick_count::now();
207  const double actualRuntime = ( endtbb - starttbb ).seconds();
208 
209  DEBUG_MSG << "Finish event " << context.evt() << " in " << int( 1000 * actualRuntime ) << " ms" << endmsg;
210 
211  DEBUG_MSG << "Timing: ExpectedCrunchtime= " << crunchtime_ms << " ms. ExpectedDreamtime= " << int( 1000 * dreamtime )
212  << " ms. ActualTotalRuntime= " << int( 1000 * actualRuntime )
213  << " ms. Ratio= " << ( crunchtime + dreamtime ) / actualRuntime << endmsg;
214 
216 
217  return StatusCode::SUCCESS;
218 }
219 
220 //------------------------------------------------------------------------------
221 
222 StatusCode CPUCruncher::finalize() // the finalization of the algorithm
223 {
224  MsgStream log( msgSvc(), name() );
225 
226  unsigned int ninstances;
227 
228  {
229  CHM::const_accessor const_name_ninstances;
230  m_name_ncopies_map.find( const_name_ninstances, name() );
231  ninstances = const_name_ninstances->second;
232  }
233 
234  constexpr double s2ms = 1000.;
235  // do not show repetitions
236  if ( ninstances != 0 ) {
237  info() << "Summary: name= " << name() << "\t avg_runtime= " << m_avg_runtime * s2ms << "\t n_clones= " << ninstances
238  << endmsg;
239 
240  CHM::accessor name_ninstances;
241  m_name_ncopies_map.find( name_ninstances, name() );
242  name_ninstances->second = 0;
243  }
244 
245  return GaudiAlgorithm::finalize();
246 }
247 
248 //------------------------------------------------------------------------------
StatusCode execute() override
the execution of the algorithm
Definition: CPUCruncher.cpp:95
SmartIF< ICPUCrunchSvc > m_crunchSvc
Definition: CPUCruncher.h:78
virtual std::chrono::milliseconds crunch_for(const std::chrono::milliseconds &) const =0
Definition of the MsgStream class used to transmit messages.
Definition: MsgStream.h:24
Gaudi::Property< float > m_sleepFraction
Definition: CPUCruncher.h:62
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:25
A class that implements a search for prime numbers.
Definition: CPUCruncher.h:19
ContextID_t slot() const
Definition: EventContext.h:48
Gaudi::Property< bool > m_loader
Definition: CPUCruncher.h:53
MsgStream & info() const
shortcut for the method msgStream(MSG::INFO)
const std::string & name() const override
The identifying name of the algorithm object.
Definition: Algorithm.cpp:635
StatusCode initialize() override
standard initialization method
bool isIOBound() const
Definition: Algorithm.h:431
virtual ~CPUCruncher()
virtual & protected desctrustor
Definition: CPUCruncher.cpp:34
void setFilterPassed(bool state) const
Set the filter passed flag to the specified state.
T to_string(T...args)
T clock(T...args)
Gaudi::Property< unsigned int > m_rwRepetitions
Definition: CPUCruncher.h:61
constexpr static const auto SUCCESS
Definition: StatusCode.h:85
void initDataHandleHolder()
initializes all handles - called by the sysInitialize method of any descendant of this ...
T sleep_for(T...args)
const std::string & context() const
Returns the "context" string. Used to identify different processing states.
Definition: GaudiCommon.h:708
Parameters for the Gauss random number generation.
std::vector< DataObjectHandle< DataObject > * > m_outputHandles
Definition: CPUCruncher.h:73
This class represents an entry point to all the event specific data.
Definition: EventContext.h:31
#define VERBOSE_MSG
Definition: CPUCruncher.cpp:19
ContextEvt_t evt() const
Definition: EventContext.h:47
STL class.
#define DECLARE_COMPONENT(type)
tbb::concurrent_hash_map< std::string, unsigned int > CHM
Definition: CPUCruncher.h:22
StatusCode service(const Gaudi::Utils::TypeNameString &name, T *&svc, bool createIf=true)
Templated method to access a service by name.
Definition: ISvcLocator.h:76
T push_back(T...args)
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
static CHM m_name_ncopies_map
Definition: CPUCruncher.h:75
Gaudi::Details::PropertyBase * declareProperty(const std::string &name, ToolHandle< T > &hndl, const std::string &doc="none")
Definition: Algorithm.h:338
This class is used for returning status codes from appropriate routines.
Definition: StatusCode.h:50
constexpr double m
Definition: SystemOfUnits.h:92
void setIOBound(bool value)
Definition: Algorithm.h:433
StatusCode finalize() override
standard finalization method
Gaudi::Property< bool > m_invertCFD
Definition: CPUCruncher.h:65
The useful base class for data processing algorithms.
std::vector< DataObjectHandle< DataObject > * > m_inputHandles
Definition: CPUCruncher.h:72
GAUDI_API const EventContext & currentContext()
const SmartIF< IMessageSvc > & msgSvc() const
The standard message service.
T fabs(T...args)
#define DEBUG_MSG
Definition: CPUCruncher.cpp:16
T size(T...args)
void declareRuntimeRequestedOutputs()
The CPU intensive function.
Definition: CPUCruncher.cpp:79
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
Gaudi::Property< double > m_avg_runtime
Definition: CPUCruncher.h:58
StatusCode initialize() override
Its initialization.
Definition: CPUCruncher.cpp:40
bool isValid() const
Allow for check if smart pointer is valid.
Definition: SmartIF.h:62
T back(T...args)
CPUCruncher()
the default constructor is disabled
constexpr static const auto FAILURE
Definition: StatusCode.h:86
Gaudi::Property< unsigned int > m_failNEvents
Definition: CPUCruncher.h:66
T hex(T...args)
SmartIF< ISvcLocator > & serviceLocator() const override
The standard service locator.
Definition: Algorithm.cpp:679
Gaudi::Property< std::vector< std::string > > m_outKeys
Definition: CPUCruncher.h:56
Gaudi::Property< std::vector< std::string > > m_inpKeys
Definition: CPUCruncher.h:55
MsgStream & fatal() const
shortcut for the method msgStream(MSG::FATAL)
A DataObject is the base class of any identifiable object on any data store.
Definition: DataObject.h:30
SmartIF< IRndmGenSvc > & randSvc() const
The standard RandomGen service, Return a pointer to the service if present.
Definition: Algorithm.cpp:672
StatusCode finalize() override
the finalization of the algorithm
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:192
Gaudi::Property< bool > m_local_rndm_gen
Definition: CPUCruncher.h:60
Gaudi::Property< double > m_var_runtime
Definition: CPUCruncher.h:59
#define ON_DEBUG
Definition: CPUCruncher.cpp:15
bool m_declAugmented
Definition: CPUCruncher.h:52