The Gaudi Framework
master (9f3f8895)
Toggle main menu visibility
Loading...
Searching...
No Matches
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
13
#include <
GaudiKernel/ISvcLocator.h
>
14
15
#include <sys/times.h>
16
#include <tbb/tick_count.h>
17
18
DECLARE_COMPONENT
(
CPUCrunchSvc
)
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
35
CPUCrunchSvc::CPUCrunchSvc
(
const
std::string&
name
,
ISvcLocator
* svc ) :
base_class
(
name
, svc ) {}
36
37
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
38
39
StatusCode
CPUCrunchSvc::initialize
() {
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
*/
58
void
CPUCrunchSvc::calibrate
() {
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
77
m_times_vect
.resize(
m_niters_vect
.value().size() );
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
120
unsigned
int
CPUCrunchSvc::getNCaliIters
( std::chrono::microseconds runtime )
const
{
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
210
std::chrono::milliseconds
CPUCrunchSvc::crunch_for
(
const
std::chrono::milliseconds& crunchtime )
const
{
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
}
CPUCrunchSvc.h
DEBUG_MSG
#define DEBUG_MSG
Definition
GPUCruncher.cpp:25
VERBOSE_MSG
#define VERBOSE_MSG
Definition
GPUCruncher.cpp:28
ISvcLocator.h
endmsg
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition
MsgStream.h:198
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition
PluginServiceV1.h:45
CPUCrunchSvc
Definition
CPUCrunchSvc.h:22
CPUCrunchSvc::calibrate
void calibrate()
Definition
CPUCrunchSvc.cpp:58
CPUCrunchSvc::m_times_vect
std::vector< unsigned int > m_times_vect
Definition
CPUCrunchSvc.h:39
CPUCrunchSvc::initialize
virtual StatusCode initialize() override
Definition
CPUCrunchSvc.cpp:39
CPUCrunchSvc::m_numCalibRuns
Gaudi::Property< int > m_numCalibRuns
Definition
CPUCrunchSvc.h:46
CPUCrunchSvc::m_shortCalib
Gaudi::Property< bool > m_shortCalib
Definition
CPUCrunchSvc.h:43
CPUCrunchSvc::CPUCrunchSvc
CPUCrunchSvc(const std::string &name, ISvcLocator *svc)
Definition
CPUCrunchSvc.cpp:35
CPUCrunchSvc::m_niters_vect
Gaudi::Property< std::vector< unsigned int > > m_niters_vect
Definition
CPUCrunchSvc.h:41
CPUCrunchSvc::findPrimes
void findPrimes(unsigned int) const
Definition
CPUCrunchSvc.cpp:158
CPUCrunchSvc::m_corrFact
Gaudi::Property< float > m_corrFact
Definition
CPUCrunchSvc.h:47
CPUCrunchSvc::m_minCalibTime
Gaudi::Property< unsigned int > m_minCalibTime
Definition
CPUCrunchSvc.h:44
CPUCrunchSvc::crunch_for
std::chrono::milliseconds crunch_for(const std::chrono::milliseconds &crunchtime) const override
Definition
CPUCrunchSvc.cpp:210
CPUCrunchSvc::getNCaliIters
unsigned int getNCaliIters(std::chrono::microseconds runtime) const
Definition
CPUCrunchSvc.cpp:120
CommonMessagingBase::error
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
Definition
CommonMessaging.h:109
CommonMessagingBase::warning
MsgStream & warning() const
shortcut for the method msgStream(MSG::WARNING)
Definition
CommonMessaging.h:112
CommonMessagingBase::debug
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
Definition
CommonMessaging.h:118
ISvcLocator
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition
ISvcLocator.h:42
Service::name
const std::string & name() const override
Retrieve name of the service.
Definition
Service.cpp:333
StatusCode
This class is used for returning status codes from appropriate routines.
Definition
StatusCode.h:64
StatusCode::SUCCESS
constexpr static const auto SUCCESS
Definition
StatusCode.h:99
StatusCode::FAILURE
constexpr static const auto FAILURE
Definition
StatusCode.h:100
extends< Service, ICPUCrunchSvc >::base_class
extends base_class
Definition
extends.h:23
std::abs
Gaudi::ParticleID abs(const Gaudi::ParticleID &p)
Return the absolute value for a PID.
Definition
ParticleID.h:191
GaudiSvc
src
CPUCrunchSvc
CPUCrunchSvc.cpp
Generated on
for The Gaudi Framework by
1.17.0