The Gaudi Framework  v39r1 (adb068b2)
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  // This is a bit ugly. There is no way to declare a vector of DataObjectHandles, so
62  // we need to wait until initialize when we've read in the input and output key
63  // properties, and know their size, and then turn them
64  // into Handles and register them with the framework by calling declareProperty. We
65  // could call declareInput/declareOutput on them too.
66 
67  int i = 0;
68  for ( auto k : m_inpKeys ) {
69  DEBUG_MSG << "adding input key " << k << endmsg;
71  declareProperty( "dummy_in_" + std::to_string( i ), *( m_inputHandles.back() ) );
72  i++;
73  }
74 
75  i = 0;
76  for ( auto k : m_outKeys ) {
77  DEBUG_MSG << "adding output key " << k << endmsg;
79  declareProperty( "dummy_out_" + std::to_string( i ), *( m_outputHandles.back() ) );
80  i++;
81  }
82 
83  return sc;
84 }
85 
86 //------------------------------------------------------------------------------
88  //
89  for ( const auto& k : outputDataObjs() ) {
90  auto outputHandle = new DataObjectHandle<DataObject>( k, Gaudi::DataHandle::Writer, this );
91  VERBOSE_MSG << "found late-attributed output: " << outputHandle->objKey() << endmsg;
92  m_outputHandles.push_back( outputHandle );
93  declareProperty( "dummy_out_" + outputHandle->objKey(), *( m_outputHandles.back() ) );
94  }
95 
97 
98  m_declAugmented = true;
99 }
100 
101 //------------------------------------------------------------------------------
102 
103 StatusCode CPUCruncher::execute() // the execution of the algorithm
104 {
105 
107 
108  double crunchtime;
109 
110  if ( m_local_rndm_gen ) {
111  /* This will disappear with a thread safe random number generator service.
112  * Use basic Box-Muller to generate Gaussian random numbers.
113  * The quality is not good for in depth study given that the generator is a
114  * linear congruent.
115  * Throw away basically a free number: we are in a cpu cruncher after all.
116  * The seed is taken from the clock, but we could assign a seed per module to
117  * ensure reproducibility.
118  *
119  * This is not an overkill but rather an exercise towards a thread safe
120  * random number generation.
121  */
122 
123  auto getGausRandom = []( double mean, double sigma ) -> double {
124  unsigned int seed = std::clock();
125 
126  auto getUnifRandom = []( unsigned int& seed ) -> double {
127  // from "Numerical Recipes"
128  constexpr unsigned int m = 232;
129  constexpr unsigned int a = 1664525;
130  constexpr unsigned int c = 1013904223;
131  seed = ( a * seed + c ) % m;
132  const double unif = double( seed ) / m;
133  return unif;
134  };
135 
136  double unif1, unif2;
137  do {
138  unif1 = getUnifRandom( seed );
139  unif2 = getUnifRandom( seed );
140  } while ( unif1 < std::numeric_limits<double>::epsilon() );
141 
142  const double normal = sqrt( -2. * log( unif1 ) ) * cos( 2 * M_PI * unif2 );
143 
144  return normal * sigma + mean;
145  };
146 
147  crunchtime = std::abs( getGausRandom( m_avg_runtime * ( 1. - m_sleepFraction ), m_var_runtime ) );
148  // End Of temp block
149  } else {
150  // Should be a member.
152  crunchtime = std::abs( rndmgaus() );
153  }
154  unsigned int crunchtime_ms = 1000 * crunchtime;
155 
156  // Prepare to sleep (even if we won't enter the following if clause for sleeping).
157  // This is needed to distribute evenly among all algorithms the overhead (around sleeping) which is harmful when
158  // trying to achieve uniform distribution of algorithm timings.
159  const double dreamtime = m_avg_runtime * m_sleepFraction;
160  const std::chrono::duration<double> dreamtime_duration( dreamtime );
161 
162  // Start to measure the total time here, together with the dreaming process straight ahead
163  tbb::tick_count starttbb = tbb::tick_count::now();
164 
165  DEBUG_MSG << "Crunching time will be: " << crunchtime_ms << " ms" << endmsg;
166  const EventContext& context = Gaudi::Hive::currentContext();
167  DEBUG_MSG << "Start event " << context.evt() << " in slot " << context.slot() << " on pthreadID " << std::hex
168  << pthread_self() << std::dec << endmsg;
169 
170  VERBOSE_MSG << "inputs number: " << m_inputHandles.size() << endmsg;
171  for ( auto& inputHandle : m_inputHandles ) {
172  if ( !inputHandle->isValid() ) continue;
173 
174  VERBOSE_MSG << "get from TS: " << inputHandle->objKey() << endmsg;
175  DataObject* obj = nullptr;
176  for ( unsigned int i = 0; i < m_rwRepetitions; ++i ) { obj = inputHandle->get(); }
177  if ( obj == nullptr ) error() << "A read object was a null pointer." << endmsg;
178  }
179 
180  if ( m_nParallel > 1 ) {
181  tbb::parallel_for( tbb::blocked_range<size_t>( 0, m_nParallel ), [&]( tbb::blocked_range<size_t> r ) {
182  m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
183  debug() << "CPUCrunch complete in TBB parallel for block " << r.begin() << " to " << r.end() << endmsg;
184  } );
185  } else {
186  m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
187  }
188 
189  // Return error on fraction of events if configured
190  if ( m_failNEvents > 0 && context.evt() > 0 && ( context.evt() % m_failNEvents ) == 0 ) {
191  return StatusCode::FAILURE;
192  }
193 
194  VERBOSE_MSG << "outputs number: " << m_outputHandles.size() << endmsg;
195  for ( auto& outputHandle : m_outputHandles ) {
196  if ( !outputHandle->isValid() ) continue;
197 
198  VERBOSE_MSG << "put to TS: " << outputHandle->objKey() << endmsg;
199  outputHandle->put( std::make_unique<DataObject>() );
200  }
201 
202  tbb::tick_count endtbb = tbb::tick_count::now();
203  const double actualRuntime = ( endtbb - starttbb ).seconds();
204 
205  DEBUG_MSG << "Finish event " << context.evt() << " in " << int( 1000 * actualRuntime ) << " ms" << endmsg;
206 
207  DEBUG_MSG << "Timing: ExpectedCrunchtime= " << crunchtime_ms << " ms. ExpectedDreamtime= " << int( 1000 * dreamtime )
208  << " ms. ActualTotalRuntime= " << int( 1000 * actualRuntime )
209  << " ms. Ratio= " << ( crunchtime + dreamtime ) / actualRuntime << endmsg;
210 
212 
213  return StatusCode::SUCCESS;
214 }
215 
216 //------------------------------------------------------------------------------
217 
218 StatusCode CPUCruncher::finalize() // the finalization of the algorithm
219 {
220  MsgStream log( msgSvc(), name() );
221 
222  unsigned int ninstances;
223 
224  {
225  CHM::const_accessor const_name_ninstances;
226  m_name_ncopies_map.find( const_name_ninstances, name() );
227  ninstances = const_name_ninstances->second;
228  }
229 
230  constexpr double s2ms = 1000.;
231  // do not show repetitions
232  if ( ninstances != 0 ) {
233  info() << "Summary: name= " << name() << "\t avg_runtime= " << m_avg_runtime * s2ms << "\t n_clones= " << ninstances
234  << endmsg;
235 
236  CHM::accessor name_ninstances;
237  m_name_ncopies_map.find( name_ninstances, name() );
238  name_ninstances->second = 0;
239  }
240 
241  return Algorithm::finalize();
242 }
243 
244 //------------------------------------------------------------------------------
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
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:28
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
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:87
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:304
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
AvalancheSchedulerErrorTest.msgSvc
msgSvc
Definition: AvalancheSchedulerErrorTest.py:80
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:103
std::to_string
T to_string(T... args)
CPUCruncher::finalize
StatusCode finalize() override
the finalization of the algorithm
Definition: CPUCruncher.cpp:218
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
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
ConditionsStallTest.name
name
Definition: ConditionsStallTest.py:77
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
OffloadAtlasMCRecoScenario.seed
seed
Definition: OffloadAtlasMCRecoScenario.py:52
EventContext
Definition: EventContext.h:34
DataObject
Definition: DataObject.h:36
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
StatusCode::FAILURE
constexpr static const auto FAILURE
Definition: StatusCode.h:101
VERBOSE_MSG
#define VERBOSE_MSG
Definition: CPUCruncher.cpp:30
CPUCruncher::m_outputHandles
std::vector< DataObjectHandle< DataObject > * > m_outputHandles
Definition: CPUCruncher.h:85
CPUCruncher::m_sleepFraction
Gaudi::Property< double > m_sleepFraction
Definition: CPUCruncher.h:73
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