The Gaudi Framework  master (82fdf313)
Loading...
Searching...
No Matches
CPUCruncher.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2025 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
22
24
25#define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
26#define DEBUG_MSG ON_DEBUG debug()
27
28#define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
29#define VERBOSE_MSG ON_VERBOSE verbose()
30
31//------------------------------------------------------------------------------
32
33CPUCruncher::CPUCruncher( const std::string& name, // the algorithm instance name
34 ISvcLocator* pSvc )
35 : Algorithm( name, pSvc ) {
36
37 // Register the algo in the static concurrent hash map in order to
38 // monitor the # of copies
39 CHM::accessor name_ninstances;
40 m_name_ncopies_map.insert( name_ninstances, name );
41 name_ninstances->second += 1;
42}
43
45 for ( uint i = 0; i < m_inputHandles.size(); ++i ) delete m_inputHandles[i];
46
47 for ( uint i = 0; i < m_outputHandles.size(); ++i ) delete m_outputHandles[i];
48}
49
51 auto sc = Algorithm::initialize();
52 if ( !sc ) return sc;
53
54 m_crunchSvc = serviceLocator()->service( "CPUCrunchSvc" );
55 if ( !m_crunchSvc.isValid() ) {
56 fatal() << "unable to acquire CPUCruncSvc" << endmsg;
58 }
59
60 // This is a bit ugly. There is no way to declare a vector of DataObjectHandles, so
61 // we need to wait until initialize when we've read in the input and output key
62 // properties, and know their size, and then turn them
63 // into Handles and register them with the framework by calling declareProperty. We
64 // could call declareInput/declareOutput on them too.
65
66 int i = 0;
67 for ( auto k : m_inpKeys ) {
68 DEBUG_MSG << "adding input key " << k << endmsg;
70 declareProperty( "dummy_in_" + std::to_string( i ), *( m_inputHandles.back() ) );
71 i++;
72 }
73
74 i = 0;
75 for ( auto k : m_outKeys ) {
76 DEBUG_MSG << "adding output key " << k << endmsg;
78 declareProperty( "dummy_out_" + std::to_string( i ), *( m_outputHandles.back() ) );
79 i++;
80 }
81
82 return sc;
83}
84
85//------------------------------------------------------------------------------
87 //
88 for ( const auto& k : outputDataObjs() ) {
89 auto outputHandle = new DataObjectHandle<DataObject>( k, Gaudi::DataHandle::Writer, this );
90 VERBOSE_MSG << "found late-attributed output: " << outputHandle->objKey() << endmsg;
91 m_outputHandles.push_back( outputHandle );
92 declareProperty( "dummy_out_" + outputHandle->objKey(), *( m_outputHandles.back() ) );
93 }
94
96
97 m_declAugmented = true;
98}
99
100//------------------------------------------------------------------------------
101
102StatusCode CPUCruncher::execute() // the execution of the algorithm
103{
104
106
107 double crunchtime;
108
109 if ( m_local_rndm_gen ) {
110 /* This will disappear with a thread safe random number generator service.
111 * Use basic Box-Muller to generate Gaussian random numbers.
112 * The quality is not good for in depth study given that the generator is a
113 * linear congruent.
114 * Throw away basically a free number: we are in a cpu cruncher after all.
115 * The seed is taken from the clock, but we could assign a seed per module to
116 * ensure reproducibility.
117 *
118 * This is not an overkill but rather an exercise towards a thread safe
119 * random number generation.
120 */
121
122 auto getGausRandom = []( double mean, double sigma ) -> double {
123 unsigned int seed = std::clock();
124
125 auto getUnifRandom = []( unsigned int& seed ) -> double {
126 // from "Numerical Recipes"
127 constexpr unsigned int m = 232;
128 constexpr unsigned int a = 1664525;
129 constexpr unsigned int c = 1013904223;
130 seed = ( a * seed + c ) % m;
131 const double unif = double( seed ) / m;
132 return unif;
133 };
134
135 double unif1, unif2;
136 do {
137 unif1 = getUnifRandom( seed );
138 unif2 = getUnifRandom( seed );
139 } while ( unif1 < std::numeric_limits<double>::epsilon() );
140
141 const double normal = sqrt( -2. * log( unif1 ) ) * cos( 2 * M_PI * unif2 );
142
143 return normal * sigma + mean;
144 };
145
146 crunchtime = std::abs( getGausRandom( m_avg_runtime * ( 1. - m_sleepFraction ), m_var_runtime ) );
147 // End Of temp block
148 } else {
149 // Should be a member.
151 crunchtime = std::abs( rndmgaus() );
152 }
153 unsigned int crunchtime_ms = 1000 * crunchtime;
154
155 // Prepare to sleep (even if we won't enter the following if clause for sleeping).
156 // This is needed to distribute evenly among all algorithms the overhead (around sleeping) which is harmful when
157 // trying to achieve uniform distribution of algorithm timings.
158 const double dreamtime = m_avg_runtime * m_sleepFraction;
159 const std::chrono::duration<double> dreamtime_duration( dreamtime );
160
161 // Start to measure the total time here, together with the dreaming process straight ahead
162 tbb::tick_count starttbb = tbb::tick_count::now();
163
164 DEBUG_MSG << "Crunching time will be: " << crunchtime_ms << " ms" << endmsg;
165 const EventContext& context = Gaudi::Hive::currentContext();
166 DEBUG_MSG << "Start event " << context.evt() << " in slot " << context.slot() << " on pthreadID " << std::hex
167 << pthread_self() << std::dec << endmsg;
168
169 VERBOSE_MSG << "inputs number: " << m_inputHandles.size() << endmsg;
170 for ( auto& inputHandle : m_inputHandles ) {
171 if ( !inputHandle->isValid() ) continue;
172
173 VERBOSE_MSG << "get from TS: " << inputHandle->objKey() << endmsg;
174 const DataObject* obj = nullptr;
175 for ( unsigned int i = 0; i < m_rwRepetitions; ++i ) { obj = inputHandle->get(); }
176 if ( obj == nullptr ) error() << "A read object was a null pointer." << endmsg;
177 }
178
179 if ( m_nParallel > 1 ) {
180 tbb::parallel_for( tbb::blocked_range<size_t>( 0, m_nParallel ), [&]( tbb::blocked_range<size_t> r ) {
181 m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
182 debug() << "CPUCrunch complete in TBB parallel for block " << r.begin() << " to " << r.end() << endmsg;
183 } );
184 } else {
185 m_crunchSvc->crunch_for( std::chrono::milliseconds( crunchtime_ms ) );
186 }
187
188 // Return error on fraction of events if configured
189 if ( m_failNEvents > 0 && context.evt() > 0 && ( context.evt() % m_failNEvents ) == 0 ) {
190 return StatusCode::FAILURE;
191 }
192
193 VERBOSE_MSG << "outputs number: " << m_outputHandles.size() << endmsg;
194 for ( auto& outputHandle : m_outputHandles ) {
195 if ( !outputHandle->isValid() ) continue;
196
197 VERBOSE_MSG << "put to TS: " << outputHandle->objKey() << endmsg;
198 outputHandle->put( std::make_unique<DataObject>() );
199 }
200
201 tbb::tick_count endtbb = tbb::tick_count::now();
202 const double actualRuntime = ( endtbb - starttbb ).seconds();
203
204 DEBUG_MSG << "Finish event " << context.evt() << " in " << int( 1000 * actualRuntime ) << " ms" << endmsg;
205
206 DEBUG_MSG << "Timing: ExpectedCrunchtime= " << crunchtime_ms << " ms. ExpectedDreamtime= " << int( 1000 * dreamtime )
207 << " ms. ActualTotalRuntime= " << int( 1000 * actualRuntime )
208 << " ms. Ratio= " << ( crunchtime + dreamtime ) / actualRuntime << endmsg;
209
211
212 return StatusCode::SUCCESS;
213}
214
215//------------------------------------------------------------------------------
216
217StatusCode CPUCruncher::finalize() // the finalization of the algorithm
218{
219 MsgStream log( msgSvc(), name() );
220
221 unsigned int ninstances;
222
223 {
224 CHM::const_accessor const_name_ninstances;
225 m_name_ncopies_map.find( const_name_ninstances, name() );
226 ninstances = const_name_ninstances->second;
227 }
228
229 constexpr double s2ms = 1000.;
230 // do not show repetitions
231 if ( ninstances != 0 ) {
232 info() << "Summary: name= " << name() << "\t avg_runtime= " << m_avg_runtime * s2ms << "\t n_clones= " << ninstances
233 << endmsg;
234
235 CHM::accessor name_ninstances;
236 m_name_ncopies_map.find( name_ninstances, name() );
237 name_ninstances->second = 0;
238 }
239
240 return Algorithm::finalize();
241}
242
243//------------------------------------------------------------------------------
#define DEBUG_MSG
#define VERBOSE_MSG
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition MsgStream.h:198
#define DECLARE_COMPONENT(type)
A class that implements a search for prime numbers.
Definition CPUCruncher.h:30
Gaudi::Property< bool > m_invertCFD
Definition CPUCruncher.h:77
std::vector< DataObjectHandle< DataObject > * > m_outputHandles
Definition CPUCruncher.h:86
CPUCruncher()
the default constructor is disabled
StatusCode execute() override
the execution of the algorithm
virtual ~CPUCruncher()
virtual & protected desctrustor
bool m_declAugmented
Definition CPUCruncher.h:63
void declareRuntimeRequestedOutputs()
The CPU intensive function.
Gaudi::Property< std::vector< std::string > > m_outKeys
Definition CPUCruncher.h:67
StatusCode finalize() override
the finalization of the algorithm
std::vector< DataObjectHandle< DataObject > * > m_inputHandles
Definition CPUCruncher.h:85
tbb::concurrent_hash_map< std::string, unsigned int > CHM
Definition CPUCruncher.h:33
static CHM m_name_ncopies_map
Definition CPUCruncher.h:88
SmartIF< ICPUCrunchSvc > m_crunchSvc
Definition CPUCruncher.h:91
Gaudi::Property< bool > m_local_rndm_gen
Definition CPUCruncher.h:71
Gaudi::Property< double > m_var_runtime
Definition CPUCruncher.h:70
Gaudi::Property< std::vector< std::string > > m_inpKeys
Definition CPUCruncher.h:66
StatusCode initialize() override
Its initialization.
Gaudi::Property< int > m_nParallel
Definition CPUCruncher.h:79
Gaudi::Property< double > m_avg_runtime
Definition CPUCruncher.h:69
Gaudi::Property< bool > m_loader
Definition CPUCruncher.h:64
Gaudi::Property< double > m_sleepFraction
Definition CPUCruncher.h:74
Gaudi::Property< unsigned int > m_rwRepetitions
Definition CPUCruncher.h:73
Gaudi::Property< unsigned int > m_failNEvents
Definition CPUCruncher.h:78
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
const SmartIF< IMessageSvc > & msgSvc() const
The standard message service.
MsgStream & fatal() const
shortcut for the method msgStream(MSG::FATAL)
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
MsgStream & info() const
shortcut for the method msgStream(MSG::INFO)
DataObjectHandle.h GaudiKernel/DataObjectHandle.h.
A DataObject is the base class of any identifiable object on any data store.
Definition DataObject.h:37
This class represents an entry point to all the event specific data.
ContextEvt_t evt() const
ContextID_t slot() const
Algorithm(std::string name, ISvcLocator *svcloc, std::string version=PACKAGE_VERSION)
Constructor.
Definition Algorithm.h:98
Gaudi::Details::PropertyBase * declareProperty(const std::string &name, ToolHandle< T > &hndl, const std::string &doc="none")
Definition Algorithm.h:286
StatusCode initialize() override
the default (empty) implementation of IStateful::initialize() method
Definition Algorithm.h:175
StatusCode finalize() override
the default (empty) implementation of IStateful::finalize() method
Definition Algorithm.h:181
SmartIF< IRndmGenSvc > & randSvc() const
The standard RandomGen service, Return a pointer to the service if present.
SmartIF< ISvcLocator > & serviceLocator() const override
The standard service locator.
const std::string & name() const override
The identifying name of the algorithm object.
void setFilterPassed(bool state) const
Set the filter passed flag to the specified state.
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition ISvcLocator.h:42
virtual SmartIF< IService > & service(const Gaudi::Utils::TypeNameString &typeName, const bool createIf=true)=0
Returns a smart pointer to a service.
Definition of the MsgStream class used to transmit messages.
Definition MsgStream.h:29
Parameters for the Gauss random number generation.
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
constexpr static const auto SUCCESS
Definition StatusCode.h:99
constexpr static const auto FAILURE
Definition StatusCode.h:100
GAUDI_API const EventContext & currentContext()
Gaudi::ParticleID abs(const Gaudi::ParticleID &p)
Return the absolute value for a PID.
Definition ParticleID.h:191