The Gaudi Framework  v38r0 (2143aa4c)
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-2023 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"
12 #include "HiveNumbers.h"
14 #include <ctime>
15 #include <sys/resource.h>
16 #include <sys/times.h>
17 #include <tbb/blocked_range.h>
18 #include <tbb/parallel_for.h>
19 #include <tbb/tick_count.h>
20 #include <thread>
21 
23 
25 
26 #define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
27 #define DEBUG_MSG ON_DEBUG debug()
28 
29 #define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
30 #define VERBOSE_MSG ON_VERBOSE verbose()
31 
32 //------------------------------------------------------------------------------
33 
34 CPUCruncher::CPUCruncher( const std::string& name, // the algorithm instance name
35  ISvcLocator* pSvc )
36  : Algorithm( name, pSvc ) {
37 
38  // Register the algo in the static concurrent hash map in order to
39  // monitor the # of copies
40  CHM::accessor name_ninstances;
41  m_name_ncopies_map.insert( name_ninstances, name );
42  name_ninstances->second += 1;
43 }
44 
46  for ( uint i = 0; i < m_inputHandles.size(); ++i ) delete m_inputHandles[i];
47 
48  for ( uint i = 0; i < m_outputHandles.size(); ++i ) delete m_outputHandles[i];
49 }
50 
52  auto sc = Algorithm::initialize();
53  if ( !sc ) return sc;
54 
55  m_crunchSvc = serviceLocator()->service( "CPUCrunchSvc" );
56  if ( !m_crunchSvc.isValid() ) {
57  fatal() << "unable to acquire CPUCruncSvc" << endmsg;
58  return StatusCode::FAILURE;
59  }
60 
61  // if an algorithm was setup to sleep, for whatever period, it effectively models CPU-blocking behavior
63 
64  // This is a bit ugly. There is no way to declare a vector of DataObjectHandles, so
65  // we need to wait until initialize when we've read in the input and output key
66  // properties, and know their size, and then turn them
67  // into Handles and register them with the framework by calling declareProperty. We
68  // could call declareInput/declareOutput on them too.
69 
70  int i = 0;
71  for ( auto k : m_inpKeys ) {
72  DEBUG_MSG << "adding input key " << k << endmsg;
74  declareProperty( "dummy_in_" + std::to_string( i ), *( m_inputHandles.back() ) );
75  i++;
76  }
77 
78  i = 0;
79  for ( auto k : m_outKeys ) {
80  DEBUG_MSG << "adding output key " << k << endmsg;
82  declareProperty( "dummy_out_" + std::to_string( i ), *( m_outputHandles.back() ) );
83  i++;
84  }
85 
86  return sc;
87 }
88 
89 //------------------------------------------------------------------------------
91  //
92  for ( const auto& k : outputDataObjs() ) {
93  auto outputHandle = new DataObjectHandle<DataObject>( k, Gaudi::DataHandle::Writer, this );
94  VERBOSE_MSG << "found late-attributed output: " << outputHandle->objKey() << endmsg;
95  m_outputHandles.push_back( outputHandle );
96  declareProperty( "dummy_out_" + outputHandle->objKey(), *( m_outputHandles.back() ) );
97  }
98 
100 
101  m_declAugmented = true;
102 }
103 
104 //------------------------------------------------------------------------------
105 
106 StatusCode CPUCruncher::execute() // the execution of the algorithm
107 {
108 
110 
111  float crunchtime;
112 
113  if ( m_local_rndm_gen ) {
114  /* This will disappear with a thread safe random number generator service.
115  * Use basic Box-Muller to generate Gaussian random numbers.
116  * The quality is not good for in depth study given that the generator is a
117  * linear congruent.
118  * Throw away basically a free number: we are in a cpu cruncher after all.
119  * The seed is taken from the clock, but we could assign a seed per module to
120  * ensure reproducibility.
121  *
122  * This is not an overkill but rather an exercise towards a thread safe
123  * random number generation.
124  */
125 
126  auto getGausRandom = []( double mean, double sigma ) -> double {
127  unsigned int seed = std::clock();
128 
129  auto getUnifRandom = []( unsigned int& seed ) -> double {
130  // from "Numerical Recipes"
131  constexpr unsigned int m = 232;
132  constexpr unsigned int a = 1664525;
133  constexpr unsigned int c = 1013904223;
134  seed = ( a * seed + c ) % m;
135  const double unif = double( seed ) / m;
136  return unif;
137  };
138 
139  double unif1, unif2;
140  do {
141  unif1 = getUnifRandom( seed );
142  unif2 = getUnifRandom( seed );
143  } while ( unif1 < std::numeric_limits<double>::epsilon() );
144 
145  const double normal = sqrt( -2. * log( unif1 ) ) * cos( 2 * M_PI * unif2 );
146 
147  return normal * sigma + mean;
148  };
149 
150  crunchtime = std::abs( getGausRandom( m_avg_runtime * ( 1. - m_sleepFraction ), m_var_runtime ) );
151  // End Of temp block
152  } else {
153  // Should be a member.
155  crunchtime = std::abs( rndmgaus() );
156  }
157  unsigned int crunchtime_ms = 1000 * crunchtime;
158 
159  // Prepare to sleep (even if we won't enter the following if clause for sleeping).
160  // This is needed to distribute evenly among all algorithms the overhead (around sleeping) which is harmful when
161  // trying to achieve uniform distribution of algorithm timings.
162  const double dreamtime = m_avg_runtime * m_sleepFraction;
163  const std::chrono::duration<double> dreamtime_duration( dreamtime );
164  tbb::tick_count startSleeptbb;
165  tbb::tick_count endSleeptbb;
166 
167  // Start to measure the total time here, together with the dreaming process straight ahead
168  tbb::tick_count starttbb = tbb::tick_count::now();
169 
170  // If the algorithm was declared as CPU-blocking, we will replace requested part of crunching with plain sleeping
171  if ( isBlocking() ) {
172  // in this block (and not in other places around) msgLevel is checked for the same reason as above, when
173  // preparing to sleep several lines above: to reduce as much as possible the overhead around sleeping
174  DEBUG_MSG << "Dreaming time will be: " << int( 1000 * dreamtime ) << " ms" << endmsg;
175 
176  ON_DEBUG startSleeptbb = tbb::tick_count::now();
177  std::this_thread::sleep_for( dreamtime_duration );
178  ON_DEBUG endSleeptbb = tbb::tick_count::now();
179 
180  // actual sleeping time can be longer due to scheduling or resource contention delays
181  ON_DEBUG {
182  const double actualDreamTime = ( endSleeptbb - startSleeptbb ).seconds();
183  debug() << "Actual dreaming time was: " << int( 1000 * actualDreamTime ) << "ms" << endmsg;
184  }
185  } // end of "sleeping block"
186 
187  DEBUG_MSG << "Crunching time will be: " << crunchtime_ms << " ms" << endmsg;
188  const EventContext& context = Gaudi::Hive::currentContext();
189  DEBUG_MSG << "Start event " << context.evt() << " in slot " << context.slot() << " on pthreadID " << std::hex
190  << pthread_self() << std::dec << endmsg;
191 
192  VERBOSE_MSG << "inputs number: " << m_inputHandles.size() << endmsg;
193  for ( auto& inputHandle : m_inputHandles ) {
194  if ( !inputHandle->isValid() ) continue;
195 
196  VERBOSE_MSG << "get from TS: " << inputHandle->objKey() << endmsg;
197  DataObject* obj = nullptr;
198  for ( unsigned int i = 0; i < m_rwRepetitions; ++i ) { obj = inputHandle->get(); }
199  if ( obj == nullptr ) error() << "A read object was a null pointer." << endmsg;
200  }
201 
202  if ( m_nParallel > 1 ) {
203  tbb::parallel_for( tbb::blocked_range<size_t>( 0, m_nParallel ), [&]( tbb::blocked_range<size_t> r ) {
204  m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
205  debug() << "CPUCrunch complete in TBB parallel for block " << r.begin() << " to " << r.end() << endmsg;
206  } );
207  } else {
208  m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
209  }
210 
211  // Return error on fraction of events if configured
212  if ( m_failNEvents > 0 && context.evt() > 0 && ( context.evt() % m_failNEvents ) == 0 ) {
213  return StatusCode::FAILURE;
214  }
215 
216  VERBOSE_MSG << "outputs number: " << m_outputHandles.size() << endmsg;
217  for ( auto& outputHandle : m_outputHandles ) {
218  if ( !outputHandle->isValid() ) continue;
219 
220  VERBOSE_MSG << "put to TS: " << outputHandle->objKey() << endmsg;
221  outputHandle->put( std::make_unique<DataObject>() );
222  }
223 
224  tbb::tick_count endtbb = tbb::tick_count::now();
225  const double actualRuntime = ( endtbb - starttbb ).seconds();
226 
227  DEBUG_MSG << "Finish event " << context.evt() << " in " << int( 1000 * actualRuntime ) << " ms" << endmsg;
228 
229  DEBUG_MSG << "Timing: ExpectedCrunchtime= " << crunchtime_ms << " ms. ExpectedDreamtime= " << int( 1000 * dreamtime )
230  << " ms. ActualTotalRuntime= " << int( 1000 * actualRuntime )
231  << " ms. Ratio= " << ( crunchtime + dreamtime ) / actualRuntime << endmsg;
232 
234 
235  return StatusCode::SUCCESS;
236 }
237 
238 //------------------------------------------------------------------------------
239 
240 StatusCode CPUCruncher::finalize() // the finalization of the algorithm
241 {
242  MsgStream log( msgSvc(), name() );
243 
244  unsigned int ninstances;
245 
246  {
247  CHM::const_accessor const_name_ninstances;
248  m_name_ncopies_map.find( const_name_ninstances, name() );
249  ninstances = const_name_ninstances->second;
250  }
251 
252  constexpr double s2ms = 1000.;
253  // do not show repetitions
254  if ( ninstances != 0 ) {
255  info() << "Summary: name= " << name() << "\t avg_runtime= " << m_avg_runtime * s2ms << "\t n_clones= " << ninstances
256  << endmsg;
257 
258  CHM::accessor name_ninstances;
259  m_name_ncopies_map.find( name_ninstances, name() );
260  name_ninstances->second = 0;
261  }
262 
263  return Algorithm::finalize();
264 }
265 
266 //------------------------------------------------------------------------------
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.
Gaudi.Configuration.log
log
Definition: Configuration.py:29
Gaudi::Algorithm::randSvc
SmartIF< IRndmGenSvc > & randSvc() const
The standard RandomGen service, Return a pointer to the service if present.
Definition: Algorithm.cpp:565
CPUCruncher::initialize
StatusCode initialize() override
Its initialization.
Definition: CPUCruncher.cpp:51
Gaudi::Hive::currentContext
GAUDI_API const EventContext & currentContext()
Definition: ThreadLocalContext.cpp:30
bug_34121.name
name
Definition: bug_34121.py:20
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:90
ISvcLocator
Definition: ISvcLocator.h:46
Gaudi::Algorithm::initialize
StatusCode initialize() override
the default (empty) implementation of IStateful::initialize() method
Definition: Algorithm.h:178
Algorithm
Alias for backward compatibility.
Definition: Algorithm.h:58
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
std::abs
Gaudi::ParticleID abs(const Gaudi::ParticleID &p)
Return the absolute value for a PID.
Definition: ParticleID.h:191
gaudirun.c
c
Definition: gaudirun.py:525
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
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
Gaudi::Units::m
constexpr double m
Definition: SystemOfUnits.h:108
EventContext::slot
ContextID_t slot() const
Definition: EventContext.h:51
CPUCruncher::execute
StatusCode execute() override
the execution of the algorithm
Definition: CPUCruncher.cpp:106
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:240
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
MsgStream
Definition: MsgStream.h:34
CPUCruncher::m_inputHandles
std::vector< DataObjectHandle< DataObject > * > m_inputHandles
Definition: CPUCruncher.h:84
ON_DEBUG
#define ON_DEBUG
Definition: CPUCruncher.cpp:26
CPUCruncher::m_name_ncopies_map
static CHM m_name_ncopies_map
Definition: CPUCruncher.h:87
ThreadLocalContext.h
Gaudi::Algorithm::finalize
StatusCode finalize() override
the default (empty) implementation of IStateful::finalize() method
Definition: Algorithm.h:184
StatusCode::SUCCESS
constexpr static const auto SUCCESS
Definition: StatusCode.h:100
DEBUG_MSG
#define DEBUG_MSG
Definition: CPUCruncher.cpp:27
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:45
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
GaudiAlg.HistoUtils.mean
mean
Definition: HistoUtils.py:903
VERBOSE_MSG
#define VERBOSE_MSG
Definition: CPUCruncher.cpp:30
CPUCruncher::m_outputHandles
std::vector< DataObjectHandle< DataObject > * > m_outputHandles
Definition: CPUCruncher.h:85
HiveRndm::HiveNumbers
Definition: HiveNumbers.h:38
std::numeric_limits
EventContext::evt
ContextEvt_t evt() const
Definition: EventContext.h:50
CPUCruncher::m_nParallel
Gaudi::Property< int > m_nParallel
Definition: CPUCruncher.h:78