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