The Gaudi Framework  master (29688f1e)
Loading...
Searching...
No Matches
AlgResourcePool.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2026 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 "AlgResourcePool.h"
12#include <Gaudi/Sequence.h>
14#include <functional>
15#include <queue>
16#include <sstream>
17
18// Instantiation of a static factory class used by clients to create instances of this service
20
21#define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
22#define DEBUG_MSG ON_DEBUG debug()
23
24//---------------------------------------------------------------------------
25
26// Destructor
28 for ( auto& algoId_algoQueue : m_algqueue_map ) {
29 auto* queue = algoId_algoQueue.second;
30 delete queue;
31 }
32}
33
34//---------------------------------------------------------------------------
35
36// Initialize the pool with the list of algorithms known to the IAlgManager
38
40 if ( !sc.isSuccess() ) warning() << "Base class could not be started" << endmsg;
41
42 // Try to recover the topAlgList from the ApplicationManager for backward-compatibility
43 if ( m_topAlgNames.value().empty() ) {
44 info() << "TopAlg list empty. Recovering the one of Application Manager" << endmsg;
45 const Gaudi::Utils::TypeNameString appMgrName( "ApplicationMgr/ApplicationMgr" );
46 SmartIF<IProperty> appMgrProps( serviceLocator()->service( appMgrName ) );
47 m_topAlgNames.assign( appMgrProps->getProperty( "TopAlg" ) );
48 }
49
50 sc = decodeTopAlgs();
51 if ( sc.isFailure() ) warning() << "Algorithms could not be properly decoded." << endmsg;
52
54
55 // Check if an algorithm requests more of a resource than is available
56 for ( const IAlgorithm* algo : m_flatUniqueAlgList ) {
57 for ( const auto& [res_name, res_required] : algo->neededResources() ) {
58 auto res = m_availableResources.find( res_name );
59 auto res_available = ( res != m_availableResources.end() ) ? res->second : 0;
60 if ( res_required > res_available ) {
61 error() << "Cannot satisfy resource requirement '" << res_name << "' for algorithm '" << algo->name()
62 << " (required: " << res_required << ", available: " << res_available << ")" << endmsg;
64 }
65 }
66 }
67
69}
70
71//---------------------------------------------------------------------------
72
74
75 StatusCode startSc = Service::start();
76 if ( !startSc.isSuccess() ) return startSc;
77
78 // sys-Start the algorithms
79 for ( IAlgorithm* ialgo : m_algList ) {
80 startSc = ialgo->sysStart();
81 if ( startSc.isFailure() ) {
82 error() << "Unable to start Algorithm: " << ialgo->name() << endmsg;
83 return startSc;
84 }
85 }
87}
88
89//---------------------------------------------------------------------------
90
91StatusCode AlgResourcePool::acquireAlgorithm( std::string_view name, IAlgorithm*& algo, bool blocking ) {
92
93 std::hash<std::string_view> hash_function;
94 size_t algo_id = hash_function( name );
95 auto itQueueIAlgPtr = m_algqueue_map.find( algo_id );
96
97 if ( itQueueIAlgPtr == m_algqueue_map.end() ) {
98 error() << "Algorithm " << name << " requested, but not recognised" << endmsg;
99 algo = nullptr;
100 return StatusCode::FAILURE;
101 }
102
103 StatusCode sc;
104 if ( blocking ) {
105 itQueueIAlgPtr->second->pop( algo );
106 } else {
107 if ( !itQueueIAlgPtr->second->try_pop( algo ) ) {
108 if ( m_countAlgInstMisses ) {
109 auto result = m_algInstanceMisses.find( name );
110 if ( result != m_algInstanceMisses.end() )
111 ++( result->second );
112 else
114 }
116 }
117 }
118
119 // Note that reentrant algorithms are not consumed so we put them
120 // back immediately in the queue at the end of this function.
121 // Now we may still be called again in between and get this
122 // error. In such a case, the Scheduler will retry later.
123 // This is of course not optimal, but should only happen very
124 // seldom and thud won't affect the global efficiency
125 if ( sc.isFailure() )
126 DEBUG_MSG << "No instance of algorithm " << name << " could be retrieved in non-blocking mode" << endmsg;
127
128 if ( sc.isSuccess() ) {
129 // Try to acquire all the resources the algorithm needs
130 if ( !algo->neededResources().empty() ) {
131 std::scoped_lock lock( m_resource_mutex );
132
133 auto tmpResources = m_availableResources; // backup resources
134 for ( const auto& [res_name, res_value] : algo->neededResources() ) {
135 auto res = m_availableResources.find( res_name );
136 if ( res != m_availableResources.end() && res->second >= res_value ) {
137 res->second -= res_value;
138 } else {
140 const auto lvl = static_cast<MSG::Level>( m_missingResourceMsgLevel.value() );
141 if ( msgLevel( lvl ) ) {
142 msgStream( lvl ) << "Failure to allocate resource '" << res_name << "' for algorithm " << name
143 << " (required: " << res_value << ", available: " << res->second << ")" << endmsg;
144 }
145 break;
146 }
147 }
148
149 // Could not acquire all resources
150 if ( sc.isFailure() ) {
151 // Restore resources
152 m_availableResources = std::move( tmpResources );
153
154 // in case of not reentrant, push it back. Reentrant ones are pushed back
155 // in all cases further down
156 if ( !algo->isReEntrant() ) { itQueueIAlgPtr->second->push( algo ); }
157 }
158 }
159
160 if ( algo->isReEntrant() ) {
161 // push back reentrant algorithms immediately as it can be reused
162 itQueueIAlgPtr->second->push( algo );
163 }
164 }
165 return sc;
166}
167
168//---------------------------------------------------------------------------
169
171
172 std::hash<std::string_view> hash_function;
173 size_t algo_id = hash_function( name );
174
175 // release resources used by the algorithm
176 {
177 std::scoped_lock lock( m_resource_mutex );
178 for ( const auto& [res_name, res_value] : algo->neededResources() ) {
179 auto res = m_availableResources.find( res_name );
180 if ( res != m_availableResources.end() ) { res->second += res_value; }
181 }
182 }
183
184 // release algorithm itself if not reentrant
185 if ( !algo->isReEntrant() ) { m_algqueue_map[algo_id]->push( algo ); }
186 return StatusCode::SUCCESS;
187}
188
189//---------------------------------------------------------------------------
190
191StatusCode AlgResourcePool::acquireResource( std::string_view name, unsigned int value ) {
192 std::scoped_lock lock( m_resource_mutex );
193 auto res = m_availableResources.find( Gaudi::StringKey( name ) );
194 if ( res != m_availableResources.end() && res->second >= value ) {
195 res->second -= value;
196 return StatusCode::SUCCESS;
197 }
198
199 return StatusCode::FAILURE;
200}
201
202//---------------------------------------------------------------------------
203
204StatusCode AlgResourcePool::releaseResource( std::string_view name, unsigned int value ) {
205 std::scoped_lock lock( m_resource_mutex );
206 auto res = m_availableResources.find( Gaudi::StringKey( name ) );
207 if ( res == m_availableResources.end() ) { return StatusCode::FAILURE; }
208
209 res->second += value;
210 return StatusCode::SUCCESS;
211}
212
213//---------------------------------------------------------------------------
214
215StatusCode AlgResourcePool::flattenSequencer( IAlgorithm* algo, std::list<IAlgorithm*>& alglist,
216 unsigned int recursionDepth ) {
217
219
220 if ( algo->isSequence() ) {
221 auto seq = dynamic_cast<Gaudi::Sequence*>( algo );
222 if ( seq == 0 ) {
223 error() << "Unable to dcast Algorithm " << algo->name() << " to a Sequence, but it has isSequence==true"
224 << endmsg;
225 return StatusCode::FAILURE;
226 }
227
228 auto subAlgorithms = seq->subAlgorithms();
229
230 // Recursively unroll
231 ++recursionDepth;
232
233 for ( auto subalgo : *subAlgorithms ) {
234 sc = flattenSequencer( subalgo, alglist, recursionDepth );
235 if ( sc.isFailure() ) {
236 error() << "Algorithm " << subalgo->name() << " could not be flattened" << endmsg;
237 return sc;
238 }
239 }
240 } else {
241 alglist.emplace_back( algo );
242 return sc;
243 }
244 return sc;
245}
246
247//---------------------------------------------------------------------------
248
250
252 if ( !algMan.isValid() ) {
253 error() << "Algorithm manager could not be properly fetched." << endmsg;
254 return StatusCode::FAILURE;
255 }
256
258
259 // Fill the top algorithm list ----
260 for ( const std::string& typeName : m_topAlgNames ) {
261 IAlgorithm* algo = algMan->algorithm( typeName, /*createIf*/ true ).get();
262 sc = algo->sysInitialize();
263 if ( sc.isFailure() ) {
264 error() << "Unable to initialize Algorithm: " << algo->name() << endmsg;
265 return sc;
266 }
267 m_topAlgList.push_back( algo );
268 }
269 // Top algorithm list filled ----
270
271 // Now we unroll it ----
272 for ( IAlgorithm* ialgo : m_topAlgList ) { sc = flattenSequencer( ialgo, m_flatUniqueAlgList ); }
273 // stupid O(N^2) unique-ification..
274 for ( auto i = begin( m_flatUniqueAlgList ); i != end( m_flatUniqueAlgList ); ++i ) {
275 auto n = next( i );
276 while ( n != end( m_flatUniqueAlgList ) ) {
277 if ( *n == *i )
278 n = m_flatUniqueAlgList.erase( n );
279 else
280 ++n;
281 }
282 }
283 ON_DEBUG {
284 debug() << "List of algorithms is: " << endmsg;
285 for ( IAlgorithm* algo : m_flatUniqueAlgList )
286 debug() << " o " << algo->type() << "/" << algo->name() << " @ " << algo << endmsg;
287 }
288
289 // Unrolled ---
290
291 // Now let's manage the clones
292 std::hash<std::string> hash_function;
293 for ( IAlgorithm* ialgo : m_flatUniqueAlgList ) {
294
295 const std::string& item_name = ialgo->name();
296 const std::string& item_type = ialgo->type();
297 size_t algo_id = hash_function( item_name );
299 m_algqueue_map[algo_id] = queue;
300
301 if ( msgLevel( MSG::VERBOSE ) ) {
302 verbose() << "Treating resource management and clones of " << item_name << endmsg;
303 }
304
305 queue->push( ialgo );
306 m_algList.push_back( ialgo );
307 if ( ialgo->isReEntrant() ) {
308 if ( ialgo->cardinality() != 0 ) {
309 info() << "Algorithm " << ialgo->name() << " is ReEntrant, but Cardinality was set to " << ialgo->cardinality()
310 << ". Only creating 1 instance" << endmsg;
311 }
312 m_n_of_allowed_instances[algo_id] = 1;
313 } else if ( ialgo->isClonable() ) {
314 m_n_of_allowed_instances[algo_id] = ialgo->cardinality();
315 } else {
316 if ( ialgo->cardinality() == 1 ) {
317 m_n_of_allowed_instances[algo_id] = 1;
318 } else {
319 if ( !m_overrideUnClonable ) {
320 info() << "Algorithm " << ialgo->name() << " is un-Clonable but Cardinality was set to "
321 << ialgo->cardinality() << ". Only creating 1 instance" << endmsg;
322 m_n_of_allowed_instances[algo_id] = 1;
323 } else {
324 warning() << "Overriding UnClonability of Algorithm " << ialgo->name() << ". Setting Cardinality to "
325 << ialgo->cardinality() << endmsg;
326 m_n_of_allowed_instances[algo_id] = ialgo->cardinality();
327 }
328 }
329 }
330 m_n_of_created_instances[algo_id] = 1;
331
332 // potentially create clones; if not lazy creation we have to do it now
333 if ( !m_lazyCreation ) {
334 for ( unsigned int i = 1, end = m_n_of_allowed_instances[algo_id]; i < end; ++i ) {
335 DEBUG_MSG << "type/name to create clone of: " << item_type << "/" << item_name << endmsg;
336 IAlgorithm* ialgoClone( nullptr );
337
338 if ( StatusCode createAlgSc = algMan->createAlgorithm( item_type, item_name, ialgoClone, /*managed*/ true,
339 /*checkIfExists*/ false );
340 createAlgSc.isFailure() ) {
341 return createAlgSc;
342 }
343 ialgoClone->setIndex( i );
344 queue->push( ialgoClone );
345 m_n_of_created_instances[algo_id] += 1;
346 }
347 }
348 }
349
350 return sc;
351}
352
353//---------------------------------------------------------------------------
354
355std::list<IAlgorithm*> AlgResourcePool::getFlatAlgList() { return m_flatUniqueAlgList; }
356
357//---------------------------------------------------------------------------
358
359std::list<IAlgorithm*> AlgResourcePool::getTopAlgList() { return m_topAlgList; }
360
361//---------------------------------------------------------------------------
363
364 std::multimap<unsigned int, std::string_view, std::greater<unsigned int>> sortedAlgInstanceMisses;
365
366 for ( auto& p : m_algInstanceMisses ) sortedAlgInstanceMisses.insert( { p.second, p.first } );
367
368 // determine optimal indentation
369 int indnt = std::to_string( sortedAlgInstanceMisses.cbegin()->first ).length();
370
371 std::ostringstream out;
372
373 out << "Hit parade of algorithm instance misses:\n"
374 << std::right << std::setfill( ' ' )
375 << " ===============================================================================\n"
376 << std::setw( indnt + 7 ) << "Misses "
377 << "| Algorithm (# of clones) \n"
378 << " ===============================================================================\n";
379
380 std::hash<std::string_view> hash_function;
381
382 out << std::right << std::setfill( ' ' );
383 for ( const auto& p : sortedAlgInstanceMisses ) {
384 out << std::setw( indnt + 7 ) << std::to_string( p.first ) + " "
385 << " " << p.second << " (" << m_n_of_allowed_instances.at( hash_function( p.second ) ) << ")\n";
386 }
387
388 info() << out.str() << endmsg;
389}
390
391//---------------------------------------------------------------------------
392
394
395 StatusCode stopSc = Service::stop();
396 if ( !stopSc.isSuccess() ) return stopSc;
397
398 // sys-Stop the algorithm
399 for ( IAlgorithm* ialgo : m_algList ) {
400 stopSc = ialgo->sysStop();
401 if ( stopSc.isFailure() ) {
402 error() << "Unable to stop Algorithm: " << ialgo->name() << endmsg;
403 return stopSc;
404 }
405 }
407
408 return StatusCode::SUCCESS;
409}
410
412 m_topAlgList.clear();
413 m_algList.clear();
414 m_flatUniqueAlgList.clear();
415 return extends::finalize();
416}
#define DEBUG_MSG
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition MsgStream.h:198
#define ON_DEBUG
#define DECLARE_COMPONENT(type)
The AlgResourcePool is a concrete implementation of the IAlgResourcePool interface.
std::list< IAlgorithm * > getFlatAlgList() override
std::map< size_t, size_t > m_n_of_allowed_instances
Gaudi::Property< bool > m_lazyCreation
std::list< IAlgorithm * > m_algList
The list of all algorithms created within the Pool which are not top.
std::map< size_t, concurrentQueueIAlgPtr * > m_algqueue_map
StatusCode initialize() override
std::mutex m_resource_mutex
StatusCode releaseAlgorithm(std::string_view name, IAlgorithm *&algo) override
Release a certain algorithm.
Gaudi::Property< std::vector< std::string > > m_topAlgNames
Gaudi::Property< bool > m_countAlgInstMisses
~AlgResourcePool() override
StatusCode acquireResource(std::string_view name, unsigned int value) override
Acquire units of a certain resource.
std::list< IAlgorithm * > m_flatUniqueAlgList
The flat list of algorithms w/o clones.
void dumpInstanceMisses() const
Dump recorded Algorithm instance misses.
Gaudi::Property< int > m_missingResourceMsgLevel
StatusCode releaseResource(std::string_view name, unsigned int value) override
Release a certain resource.
std::list< IAlgorithm * > getTopAlgList() override
StatusCode finalize() override
tbb::concurrent_bounded_queue< IAlgorithm * > concurrentQueueIAlgPtr
Gaudi::Property< IAlgorithm::AlgResources_t > m_availableResources
std::list< IAlgorithm * > m_topAlgList
The list of top algorithms.
StatusCode stop() override
std::map< size_t, unsigned int > m_n_of_created_instances
StatusCode acquireAlgorithm(std::string_view name, IAlgorithm *&algo, bool blocking=false) override
Acquire a certain algorithm using its name.
std::unordered_map< std::string_view, unsigned int > m_algInstanceMisses
Counters for Algorithm instance misses.
StatusCode start() override
StatusCode decodeTopAlgs()
Decode the top Algorithm list.
Gaudi::Property< bool > m_overrideUnClonable
StatusCode flattenSequencer(IAlgorithm *sequencer, std::list< IAlgorithm * > &alglist, unsigned int recursionDepth=0)
Recursively flatten an algList.
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
MsgStream & verbose() const
shortcut for the method msgStream(MSG::VERBOSE)
MsgStream & warning() const
shortcut for the method msgStream(MSG::WARNING)
MsgStream & msgStream() const
Return an uninitialized MsgStream.
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
MsgStream & info() const
shortcut for the method msgStream(MSG::INFO)
MSG::Level msgLevel() const
get the cached level (originally extracted from the embedded MsgStream)
Helper class for efficient "key" access for strings.
Definition StringKey.h:67
Helper class to parse a string of format "type/name".
The IAlgorithm is the interface implemented by the Algorithm base class.
Definition IAlgorithm.h:37
virtual const AlgResources_t & neededResources() const =0
Named, non thread-safe resources used during event processing.
virtual StatusCode sysInitialize()=0
Initialization method invoked by the framework.
virtual bool isSequence() const =0
Are we a Sequence?
virtual void setIndex(const unsigned int &idx)=0
Set instantiation index of Alg.
virtual bool isReEntrant() const =0
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition Service.cpp:336
const std::string & name() const override
Retrieve name of the service.
Definition Service.cpp:333
StatusCode stop() override
Definition Service.cpp:181
SmartIF< IFace > service(const std::string &name, bool createIf=true) const
Definition Service.h:79
StatusCode start() override
Definition Service.cpp:187
StatusCode initialize() override
Definition Service.cpp:118
Small smart pointer class with automatic reference counting for IInterface.
Definition SmartIF.h:28
TYPE * get() const
Get interface pointer.
Definition SmartIF.h:82
bool isValid() const
Allow for check if smart pointer is valid.
Definition SmartIF.h:69
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
bool isFailure() const
Definition StatusCode.h:118
bool isSuccess() const
Definition StatusCode.h:302
constexpr static const auto SUCCESS
Definition StatusCode.h:99
constexpr static const auto FAILURE
Definition StatusCode.h:100
@ VERBOSE
Definition IMessageSvc.h:22