The Gaudi Framework  master (37c0b60a)
CPUCrunchSvc.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2024 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 "CPUCrunchSvc.h"
12 
14 
15 #include <sys/times.h>
16 #include <tbb/tick_count.h>
17 
19 
20 #define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
21 #define DEBUG_MSG ON_DEBUG debug()
22 
23 #define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
24 #define VERBOSE_MSG ON_VERBOSE verbose()
25 
26 namespace {
27  // idea coming from The art of computer programming by Knuth
28  constexpr bool essentiallyEqual( double const a, double const b ) {
29  return std::abs( a - b ) <= std::min( std::abs( a ), std::abs( b ) ) * std::numeric_limits<double>::epsilon();
30  }
31 } // namespace
32 
33 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
34 
36 
37 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
38 
40  debug() << "initialize" << endmsg;
41 
42  if ( base_class::initialize().isFailure() ) {
43  error() << "Error initializing base class" << endmsg;
44  return StatusCode::FAILURE;
45  }
46 
47  calibrate();
48 
49  return StatusCode::SUCCESS;
50 }
51 
52 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
53 
54 /*
55 Calibrate the crunching finding the right relation between max number to be searched and time spent.
56 The relation is a sqrt for times greater than 10^-4 seconds.
57 */
59  if ( m_niters_vect.value().size() == 0 ) {
60  m_niters_vect = { 0, 500, 600, 700, 800, 1000, 1300, 1600, 2000, 2300,
61  2600, 3000, 3300, 3500, 3900, 4200, 5000, 6000, 8000, 10000,
62  12000, 15000, 17000, 20000, 25000, 30000, 35000, 40000, 50000, 60000 };
63  if ( !m_shortCalib ) {
64  m_niters_vect.value().push_back( 80000 );
65  m_niters_vect.value().push_back( 100000 );
66  m_niters_vect.value().push_back( 150000 );
67  m_niters_vect.value().push_back( 200000 );
68  }
69  }
70 
71  if ( m_niters_vect.value().at( 0 ) != 0 ) {
72  warning() << "NIterationsVect[0]= " << m_niters_vect.value().at( 0 ) << " but needs to be zero. resetting it."
73  << endmsg;
74  m_niters_vect.value().at( 0 ) = 0;
75  }
76 
78  m_times_vect.at( 0 ) = 0;
79 
80  const unsigned int minCalibTime_us = m_minCalibTime * 1000;
81 
82  // warm it up by doing 20k iterations
83  findPrimes( 20000 );
84 
85  for ( int irun = 0; irun < m_numCalibRuns; ++irun ) {
86 
87  debug() << "Starting calibration run " << irun + 1 << " ..." << endmsg;
88  for ( unsigned int i = 1; i < m_niters_vect.value().size(); ++i ) {
89  unsigned int niters = m_niters_vect.value().at( i );
90  unsigned int trials = 30;
91  do {
92  auto start_cali = tbb::tick_count::now();
93  findPrimes( niters );
94  auto stop_cali = tbb::tick_count::now();
95  auto deltat = ( stop_cali - start_cali ).seconds();
96  m_times_vect.at( i ) = deltat * 1000000; // in microseconds
97  debug() << " Calibration: # iters = " << niters << " => " << m_times_vect.at( i ) << " us" << endmsg;
98  trials--;
99  } while ( trials > 0 && m_times_vect.at( i ) < m_times_vect.at( i - 1 ) ); // make sure that they are monotonic
100 
101  if ( i == m_niters_vect.value().size() - 1 && minCalibTime_us != 0 ) {
102  if ( m_times_vect.at( i ) < minCalibTime_us ) {
103  debug() << " increasing calib vect with " << int( m_niters_vect.value().back() * 1.2 )
104  << " iterations to reach min calib time of " << m_minCalibTime.value() << " ms " << endmsg;
105  m_niters_vect.value().push_back( int( m_niters_vect.value().back() * 1.2 ) );
106  m_times_vect.push_back( 0. );
107  }
108  }
109  }
110  }
111  if ( essentiallyEqual( m_corrFact, 1. ) ) {
112  debug() << "Adjusting times with correction factor " << m_corrFact.value() << endmsg;
113  for ( auto& t : m_times_vect ) { t = t * m_corrFact; }
114  }
115  debug() << "Calibration finished!" << endmsg;
116 }
117 
118 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
119 
121 
122  unsigned int smaller_i = 0;
123  double time = 0.;
124  bool found = false;
125  double corrRuntime = runtime.count(); // * m_corrFact;
126  // We know that the first entry is 0, so we start to iterate from 1
127  for ( unsigned int i = 1; i < m_times_vect.size(); i++ ) {
128  time = m_times_vect.at( i );
129  if ( time > corrRuntime ) {
130  smaller_i = i - 1;
131  found = true;
132  break;
133  }
134  }
135 
136  // Case 1: we are outside the interpolation range, we take the last 2 points
137  if ( not found ) smaller_i = m_times_vect.size() - 2;
138 
139  // Case 2: we maeke a linear interpolation
140  // y=mx+q
141  const auto x0 = m_times_vect.at( smaller_i );
142  const auto x1 = m_times_vect.at( smaller_i + 1 );
143  const auto y0 = m_niters_vect.value().at( smaller_i );
144  const auto y1 = m_niters_vect.value().at( smaller_i + 1 );
145  const double m = (double)( y1 - y0 ) / (double)( x1 - x0 );
146  const double q = y0 - m * x0;
147 
148  const unsigned int nCaliIters = m * corrRuntime + q;
149 
150  VERBOSE_MSG << "x0: " << x0 << " x1: " << x1 << " y0: " << y0 << " y1: " << y1 << " m: " << m << " q: " << q
151  << " itr: " << nCaliIters << endmsg;
152 
153  return nCaliIters;
154 }
155 
156 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
157 
158 void CPUCrunchSvc::findPrimes( const unsigned int n_iterations ) const {
159  // Flag to trigger the allocation
160  bool is_prime;
161 
162  // Let's prepare the material for the allocations
163  unsigned int primes_size = 1;
164  unsigned long* primes = new unsigned long[primes_size];
165  primes[0] = 2;
166 
167  unsigned long i = 2;
168 
169  // Loop on numbers
170  for ( unsigned long int iiter = 0; iiter < n_iterations; iiter++ ) {
171  // Once at max, it returns to 0
172  i += 1;
173 
174  // Check if it can be divided by the smaller ones
175  is_prime = true;
176  for ( unsigned long j = 2; j < i && is_prime; ++j ) {
177  if ( i % j == 0 ) is_prime = false;
178  } // end loop on numbers < than tested one
179 
180  if ( is_prime ) {
181  // copy the array of primes (INEFFICIENT ON PURPOSE!)
182  unsigned int new_primes_size = 1 + primes_size;
183  unsigned long* new_primes = new unsigned long[new_primes_size];
184 
185  for ( unsigned int prime_index = 0; prime_index < primes_size; prime_index++ ) {
186  new_primes[prime_index] = primes[prime_index];
187  }
188  // attach the last prime
189  new_primes[primes_size] = i;
190 
191  // Update primes array
192  delete[] primes;
193  primes = new_primes;
194  primes_size = new_primes_size;
195  } // end is prime
196 
197  } // end of while loop
198 
199  // Fool Compiler optimisations:
200  for ( unsigned int prime_index = 0; prime_index < primes_size; prime_index++ )
201  if ( primes[prime_index] == 4 )
202  debug() << "This does never happen, but it's necessary too fool aggressive compiler optimisations!" << endmsg;
203 
204  delete[] primes;
205 }
206 
207 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
208 
209 // Translate the required crunch time into interations, and do it
211 
212  const unsigned int niters = getNCaliIters( crunchtime );
213 
214  auto start_cali = tbb::tick_count::now();
215  findPrimes( niters );
216  auto stop_cali = tbb::tick_count::now();
217 
218  std::chrono::milliseconds actual( int( 1000 * ( stop_cali - start_cali ).seconds() ) );
219 
220  DEBUG_MSG << "crunch for " << crunchtime.count() << " ms == " << niters << " iter. actual time: " << actual.count()
221  << " ms. ratio: " << float( actual.count() ) / crunchtime.count() << endmsg;
222 
223  return actual;
224 }
std::vector::resize
T resize(T... args)
CPUCrunchSvc::m_minCalibTime
Gaudi::Property< unsigned int > m_minCalibTime
Definition: CPUCrunchSvc.h:45
CPUCrunchSvc::crunch_for
std::chrono::milliseconds crunch_for(const std::chrono::milliseconds &crunchtime) const override
Definition: CPUCrunchSvc.cpp:210
std::string
STL class.
details::size
constexpr auto size(const T &, Args &&...) noexcept
Definition: AnyDataWrapper.h:23
std::vector::size
T size(T... args)
ISvcLocator
Definition: ISvcLocator.h:46
std::chrono::microseconds
std::abs
Gaudi::ParticleID abs(const Gaudi::ParticleID &p)
Return the absolute value for a PID.
Definition: ParticleID.h:191
std::vector::push_back
T push_back(T... args)
VERBOSE_MSG
#define VERBOSE_MSG
Definition: CPUCrunchSvc.cpp:24
bug_34121.t
t
Definition: bug_34121.py:31
StatusCode
Definition: StatusCode.h:65
std::vector::at
T at(T... args)
Gaudi::Units::m
constexpr double m
Definition: SystemOfUnits.h:108
ProduceConsume.j
j
Definition: ProduceConsume.py:104
CPUCrunchSvc::getNCaliIters
unsigned int getNCaliIters(std::chrono::microseconds runtime) const
Definition: CPUCrunchSvc.cpp:120
CommonMessaging
Definition: CommonMessaging.h:66
CPUCrunchSvc::m_shortCalib
Gaudi::Property< bool > m_shortCalib
Definition: CPUCrunchSvc.h:44
Gaudi::Property::value
const ValueType & value() const
Definition: Property.h:237
CPUCrunchSvc::m_corrFact
Gaudi::Property< float > m_corrFact
Definition: CPUCrunchSvc.h:48
CPUCrunchSvc::calibrate
void calibrate()
Definition: CPUCrunchSvc.cpp:58
CPUCrunchSvc
Definition: CPUCrunchSvc.h:23
endmsg
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:202
CPUCrunchSvc.h
plotSpeedupsPyRoot.time
time
Definition: plotSpeedupsPyRoot.py:180
std::numeric_limits::epsilon
T epsilon(T... args)
std::min
T min(T... args)
CPUCrunchSvc::m_numCalibRuns
Gaudi::Property< int > m_numCalibRuns
Definition: CPUCrunchSvc.h:47
CPUCrunchSvc::findPrimes
void findPrimes(unsigned int) const
Definition: CPUCrunchSvc.cpp:158
StatusCode::SUCCESS
constexpr static const auto SUCCESS
Definition: StatusCode.h:100
ConditionsStallTest.name
name
Definition: ConditionsStallTest.py:77
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition: PluginServiceV1.h:46
std::chrono::microseconds::count
T count(T... args)
CPUCrunchSvc::m_times_vect
std::vector< unsigned int > m_times_vect
Definition: CPUCrunchSvc.h:40
CPUCrunchSvc::CPUCrunchSvc
CPUCrunchSvc(const std::string &name, ISvcLocator *svc)
Definition: CPUCrunchSvc.cpp:35
CPUCrunchSvc::initialize
virtual StatusCode initialize() override
Definition: CPUCrunchSvc.cpp:39
StatusCode::FAILURE
constexpr static const auto FAILURE
Definition: StatusCode.h:101
DEBUG_MSG
#define DEBUG_MSG
Definition: CPUCrunchSvc.cpp:21
ISvcLocator.h
CPUCrunchSvc::m_niters_vect
Gaudi::Property< std::vector< unsigned int > > m_niters_vect
Definition: CPUCrunchSvc.h:42