The Gaudi Framework  master (9f3f8895)
Loading...
Searching...
No Matches
PrecedenceSvc.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
12// Local includes
13#include "PrecedenceSvc.h"
14#include "EventSlot.h"
18
19// Framework includes
20#include <Gaudi/Algorithm.h>
21#include <Gaudi/Sequence.h>
22
23// STL includes
24#include <cstdlib>
25#include <filesystem>
26#include <fstream>
27#include <unistd.h>
28
29#define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
30#define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
31
33
34// ============================================================================
35// Initialization
36// ============================================================================
38 using namespace concurrency;
39
40 auto sc = Service::initialize(); // parent class must be initialized first
41 if ( sc.isFailure() ) {
42 fatal() << "Base class failed to initialize" << endmsg;
43 return sc;
44 }
45
46 // prepare a directory to dump precedence analysis files to.
48 std::string pattern = "precedence.analysis.XXXXXX";
49 if ( !mkdtemp( pattern.data() ) ) {
50 error() << "Could not create directory \"precedence.analysis.XXXXXX\" required for task precedence tracing"
51 << endmsg;
53 }
54 m_dumpDirName = pattern; // Save the generated path
55 }
56
57 if ( m_dumpPrecRules ) m_PRGraph.enableAnalysis();
58
59 // Get the algo resource pool
60 m_algResourcePool = serviceLocator()->service( "AlgResourcePool" );
61 if ( !m_algResourcePool.isValid() ) {
62 fatal() << "Error retrieving AlgoResourcePool" << endmsg;
64 }
65
66 info() << "Assembling CF and DF task precedence rules" << endmsg;
67
68 ON_DEBUG debug() << "Assembling CF precedence realm:" << endmsg;
69 // create the root CF node
70 m_PRGraph.addHeadNode( "RootDecisionHub", Concurrent{ true }, PromptDecision{ false }, ModeOr{ true },
71 AllPass{ true }, Inverted{ false } );
72 // assemble the CF rules
73 for ( const auto& ialgoPtr : m_algResourcePool->getTopAlgList() ) {
74 auto algorithm = dynamic_cast<Gaudi::Algorithm*>( ialgoPtr );
75 if ( !algorithm ) fatal() << "Conversion from IAlgorithm to Gaudi::Algorithm failed" << endmsg;
76 sc = assembleCFRules( algorithm, "RootDecisionHub" );
77 if ( sc.isFailure() ) {
78 fatal() << "Could not assemble the CF precedence realm" << endmsg;
79 return sc;
80 }
81 }
82
83 if ( m_ignoreDFRules ) {
84 warning() << "Ignoring DF precedence rules, disabling all associated features" << endmsg;
86 }
87
88 ON_DEBUG debug() << "Assembling DF precedence realm:" << endmsg;
89 sc = m_PRGraph.initialize();
90 if ( sc.isFailure() ) {
91 fatal() << "Could not assemble the DF precedence realm" << endmsg;
92 return sc;
93 }
94
95 // Rank algorithms if a prioritization rule is supplied
96 if ( m_mode == "PCE" ) {
98 m_PRGraph.rankAlgorithms( ranker );
99 } else if ( m_mode == "COD" ) {
101 m_PRGraph.rankAlgorithms( ranker );
102 } else if ( m_mode == "E" ) {
103 auto ranker = concurrency::RankerByEccentricity();
104 m_PRGraph.rankAlgorithms( ranker );
105 } else if ( m_mode == "T" ) {
106 auto ranker = concurrency::RankerByTiming();
107 m_PRGraph.rankAlgorithms( ranker );
108 } else if ( m_mode == "DRE" ) {
110 m_PRGraph.rankAlgorithms( ranker );
111 } else if ( !m_mode.empty() ) {
112 error() << "Requested prioritization rule '" << m_mode << "' is unknown" << endmsg;
113 return StatusCode::FAILURE;
114 }
115
116 if ( m_showDataFlow ) { debug() << m_PRGraph.dumpDataFlow() << endmsg; }
117
118 if ( m_verifyRules ) {
119 ON_DEBUG debug() << "Verifying task precedence rules" << endmsg;
120
121 // Check if CF properties are self-consistent
122 auto propValidator = concurrency::NodePropertiesValidator();
123 m_PRGraph.accept( propValidator );
124 if ( !propValidator.passed() )
125 warning() << propValidator.reply() << endmsg;
126 else
127 ON_DEBUG debug() << propValidator.reply() << endmsg;
128
129 // Check for violations in the DF topology
130 auto prodValidator = concurrency::ProductionAmbiguityFinder();
131 m_PRGraph.accept( prodValidator );
132 if ( !prodValidator.passed() ) {
133 error() << prodValidator.reply() << endmsg;
134 return StatusCode::FAILURE;
135 } else {
136 ON_DEBUG debug() << prodValidator.reply() << endmsg;
137 }
138
139 auto sccFinder = concurrency::TarjanSCCFinder();
140 m_PRGraph.accept( sccFinder );
141 if ( !sccFinder.passed() ) {
142 error() << sccFinder.reply() << endmsg;
143 return StatusCode::FAILURE;
144 } else {
145 ON_DEBUG debug() << sccFinder.reply() << endmsg;
146 }
147 }
148
149 if ( sc.isSuccess() ) info() << "PrecedenceSvc initialized successfully" << endmsg;
150
151 return sc;
152}
153
154// ============================================================================
155StatusCode PrecedenceSvc::assembleCFRules( Gaudi::Algorithm* algo, const std::string& parentName,
156 unsigned int recursionDepth ) {
157 using namespace concurrency;
158
160
161 ++recursionDepth;
162
163 bool isGaudiSequencer( false );
164 bool isAthSequencer( false );
165
166 if ( !algo->isSequence() ) {
167 ON_DEBUG debug() << std::string( recursionDepth, ' ' ) << "Algorithm '" << algo->name() << "' discovered" << endmsg;
168 sc = m_PRGraph.addAlgorithmNode( algo, parentName );
169 return sc;
170 } else {
171 if ( algo->hasProperty( "ShortCircuit" ) )
172 isGaudiSequencer = true;
173 else if ( algo->hasProperty( "StopOverride" ) )
174 isAthSequencer = true;
175 }
176
177 auto seq = dynamic_cast<Gaudi::Sequence*>( algo );
178 if ( seq == 0 ) {
179 error() << "Algorithm " << algo->name() << " has isSequence==true, but unable to dcast to Sequence" << endmsg;
180 return StatusCode::FAILURE;
181 }
182
183 auto subAlgorithms = seq->subAlgorithms();
184
185 // Recursively unroll
186 ON_DEBUG debug() << std::string( recursionDepth, ' ' ) << "Decision hub '" << algo->name() << "' discovered"
187 << endmsg;
188 bool modeOr = false;
189 bool allPass = false;
190 bool promptDecision = false;
191 bool isSequential = false;
192 bool isInverted = false;
193
194 if ( isGaudiSequencer ) {
195 modeOr = ( algo->getProperty( "ModeOR" ).toString() == "True" );
196 allPass = ( algo->getProperty( "IgnoreFilterPassed" ).toString() == "True" );
197 promptDecision = ( algo->getProperty( "ShortCircuit" ).toString() == "True" );
198 isInverted = ( algo->getProperty( "Invert" ).toString() == "True" );
199 if ( allPass ) promptDecision = false; // standard Gaudi::Sequencer behavior on all pass is to execute everything
200 isSequential = ( algo->hasProperty( "Sequential" ) && ( algo->getProperty( "Sequential" ).toString() == "True" ) );
201 } else if ( isAthSequencer ) {
202 modeOr = ( algo->getProperty( "ModeOR" ).toString() == "True" );
203 allPass = ( algo->getProperty( "IgnoreFilterPassed" ).toString() == "True" );
204 promptDecision = ( algo->getProperty( "StopOverride" ).toString() == "False" );
205 isSequential = ( algo->hasProperty( "Sequential" ) && ( algo->getProperty( "Sequential" ).toString() == "True" ) );
206 }
207 sc = m_PRGraph.addDecisionHubNode( algo, parentName, Concurrent{ !isSequential }, PromptDecision{ promptDecision },
208 ModeOr{ modeOr }, AllPass{ allPass }, Inverted{ isInverted } );
209 if ( sc.isFailure() ) {
210 error() << "Failed to add DecisionHub " << algo->name() << " to graph of precedence rules" << endmsg;
211 return sc;
212 }
213
214 for ( auto subalgo : *subAlgorithms ) {
215 sc = assembleCFRules( subalgo, algo->name(), recursionDepth );
216 if ( sc.isFailure() ) {
217 error() << "Algorithm " << subalgo->name() << " could not be flattened" << endmsg;
218 return sc;
219 }
220 }
221 return sc;
222}
223
224// ============================================================================
226
227 if ( Cause::source::Task == cause.m_source ) {
228 ON_VERBOSE verbose() << "Triggering bottom-up traversal at node '" << cause.m_sourceName << "'" << endmsg;
229 auto visitor = concurrency::DecisionUpdater( slot, cause, m_dumpPrecTrace );
230 m_PRGraph.getAlgorithmNode( cause.m_sourceName )->accept( visitor );
231 } else {
232 ON_VERBOSE verbose() << "Triggering top-down traversal at the root node" << endmsg;
233 auto visitor = concurrency::Supervisor( slot, cause, m_dumpPrecTrace );
234 m_PRGraph.getHeadNode()->accept( visitor );
235 }
236
237 if ( m_dumpPrecTrace )
238 if ( CFRulesResolved( slot.parentSlot ? *slot.parentSlot : slot ) )
239 dumpPrecedenceTrace( slot.parentSlot ? *slot.parentSlot : slot );
240
241 if ( m_dumpPrecRules )
242 if ( CFRulesResolved( slot.parentSlot ? *slot.parentSlot : slot ) )
243 dumpPrecedenceRules( slot.parentSlot ? *slot.parentSlot : slot );
244
245 return StatusCode::SUCCESS;
246}
247
248// ============================================================================
250
251 Cause cs = { Cause::source::Root, "RootDecisionHub" };
252 auto visitor = concurrency::RunSimulator( slot, cs );
253
254 auto& nodeDecisions = slot.controlFlowState;
255
256 std::vector<int> prevNodeDecisions;
257 int cntr = 0;
258 std::vector<int> counters;
259
260 while ( !CFRulesResolved( slot ) ) {
261 cntr += 1;
262 int prevAlgosNum = visitor.m_nodesSucceeded;
263 ON_DEBUG debug() << " Proceeding with iteration #" << cntr << endmsg;
264 prevNodeDecisions = slot.controlFlowState;
265 m_PRGraph.getHeadNode()->accept( visitor );
266 if ( prevNodeDecisions == nodeDecisions ) {
267 error() << " No progress on iteration " << cntr << " detected, node decisions are:" << nodeDecisions << endmsg;
268 return StatusCode::FAILURE;
269 }
270 info() << " Iteration #" << cntr << " finished, total algorithms executed: " << visitor.m_nodesSucceeded
271 << endmsg;
272
273 std::stringstream s;
274 s << cntr << ", " << ( visitor.m_nodesSucceeded - prevAlgosNum ) << "\n";
275
276 std::ofstream myfile;
277 myfile.open( "RunSimulation.csv", std::ios::app );
278 myfile << s.str();
279 myfile.close();
280
281 if ( visitor.m_nodesSucceeded != prevAlgosNum ) counters.push_back( visitor.m_nodesSucceeded );
282 }
283
284 info() << "Asymptotical intra-event speedup: " << (float)visitor.m_nodesSucceeded / (float)counters.size() << endmsg;
285
286 // Reset algorithm states and node decisions
287 slot.algsStates.reset();
288 nodeDecisions.assign( nodeDecisions.size(), -1 );
289
290 return StatusCode::SUCCESS;
291}
292
293// ============================================================================
295 return ( -1 != slot.controlFlowState[m_PRGraph.getHeadNode()->getNodeIndex()] ? true : false );
296}
297
298// ============================================================================
300
301 info() << std::endl << "==================== Control Flow Configuration ==================" << std::endl << std::endl;
302 info() << m_PRGraph.dumpControlFlow() << endmsg;
303}
304// ============================================================================
306 info() << std::endl << "===================== Data Flow Configuration ====================" << std::endl;
307 info() << m_PRGraph.dumpDataFlow() << endmsg;
308}
309
310// ============================================================================
311const std::string PrecedenceSvc::printState( EventSlot& slot ) const {
312
313 std::stringstream ss;
314 m_PRGraph.printState( ss, slot, 0 );
315 return ss.str();
316}
317
318// ============================================================================
320
321 if ( !m_dumpPrecRules ) {
322 warning() << "To trace temporal and topological aspects of execution flow, "
323 << "set DumpPrecedenceRules property to True " << endmsg;
324 return;
325 }
326
327 ON_DEBUG debug() << "Dumping temporal precedence rules" << endmsg;
328
329 std::string fileName;
330 if ( m_dumpPrecRulesFile.empty() ) {
331 const auto& eventID = slot.eventContext->eventID();
332 fileName = "rules_evt-" + std::to_string( eventID.event_number() ) + "_slot-" +
333 std::to_string( slot.eventContext->slot() ) + "_run-" + std::to_string( eventID.run_number() ) +
334 ".graphml";
335 } else {
336 fileName = m_dumpPrecRulesFile;
337 }
338
339 std::filesystem::path pth{ m_dumpDirName };
340 pth /= fileName;
341
342 m_PRGraph.dumpPrecRules( pth, slot );
343}
344
345// ============================================================================
347
348 if ( !m_dumpPrecTrace ) {
349 warning() << "To trace task precedence patterns, set DumpPrecedenceTrace "
350 << "property to True " << endmsg;
351 return;
352 }
353
354 ON_DEBUG debug() << "Dumping temporal precedence trace" << endmsg;
355
356 std::string fileName;
357 if ( m_dumpPrecTraceFile.empty() ) {
358 const auto& eventID = slot.eventContext->eventID();
359 fileName = "trace_evt-" + std::to_string( eventID.event_number() ) + "_slot-" +
360 std::to_string( slot.eventContext->slot() ) + "_run-" + std::to_string( eventID.run_number() ) +
361 ".graphml";
362 } else {
363 fileName = m_dumpPrecTraceFile;
364 }
365
366 std::filesystem::path pth{ m_dumpDirName };
367 pth /= fileName;
368
369 m_PRGraph.dumpPrecTrace( pth, slot );
370}
371
372// ============================================================================
373// Finalize
374// ============================================================================
#define ON_VERBOSE
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)
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 & 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)
const EventIDBase & eventID() const
ContextID_t slot() const
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:87
const std::string & name() const override
The identifying name of the algorithm object.
bool isSequence() const override
Are we a Sequence?
Definition Algorithm.h:195
A service to resolve the task execution precedence.
PrecedenceSvc(const std::string &name, ISvcLocator *svcLoc)
Constructor.
void dumpControlFlow() const override
Dump precedence rules.
Gaudi::Property< bool > m_showDataFlow
concurrency::PrecedenceRulesGraph m_PRGraph
Graph of precedence rules.
Gaudi::Property< bool > m_dumpPrecTrace
Gaudi::Property< std::string > m_dumpPrecTraceFile
Gaudi::Property< bool > m_ignoreDFRules
Scheduling strategy.
Gaudi::Property< bool > m_dumpPrecRules
StatusCode iterate(EventSlot &, const Cause &) override
Infer the precedence effect caused by an execution flow event.
Gaudi::Property< std::string > m_dumpPrecRulesFile
const std::string printState(EventSlot &) const override
StatusCode finalize() override
Finalize.
Gaudi::Property< bool > m_verifyRules
StatusCode simulate(EventSlot &) const override
Simulate execution flow.
SmartIF< IAlgResourcePool > m_algResourcePool
A shortcut to the algorithm resource pool.
void dumpPrecedenceRules(const EventSlot &) override
Dump precedence rules (available only in DEBUG mode, and must be enabled with the corresponding servi...
Gaudi::Property< std::string > m_mode
Scheduling strategy.
bool CFRulesResolved(EventSlot &) const override
Check if the root CF decision is resolved.
StatusCode initialize() override
Initialize.
void dumpPrecedenceTrace(const EventSlot &) override
Dump precedence trace (available only in DEBUG mode, and must be enabled with the corresponding servi...
std::filesystem::path m_dumpDirName
Precedence analysis facilities.
StatusCode assembleCFRules(Gaudi::Algorithm *, const std::string &, unsigned int recursionDepth=0)
void dumpDataFlow() const override
StatusCode getProperty(Gaudi::Details::PropertyBase *p) const override
get the property
bool hasProperty(std::string_view name) const override
Return true if we have a property with the given name.
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition Service.cpp:336
StatusCode finalize() override
Definition Service.cpp:223
StatusCode initialize() override
Definition Service.cpp:118
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
bool isFailure() const
Definition StatusCode.h:118
constexpr static const auto SUCCESS
Definition StatusCode.h:99
constexpr static const auto FAILURE
Definition StatusCode.h:100
The visitor implements the Tarjan algorithm for searching strongly connected components in the data f...
Definition Validators.h:168
Gaudi::tagged_bool< class AllPass_tag > AllPass
Gaudi::tagged_bool< class Concurrent_tag > Concurrent
Gaudi::tagged_bool< class ModeOr_tag > ModeOr
Gaudi::tagged_bool< class PromptDecision_tag > PromptDecision
Gaudi::tagged_bool< class Inverted_tag > Inverted
std::string m_sourceName
Class representing an event slot.
Definition EventSlot.h:23
std::unique_ptr< EventContext > eventContext
Cache for the eventContext.
Definition EventSlot.h:82
EventSlot * parentSlot
Pointer to parent slot (null for top level).
Definition EventSlot.h:95
AlgsExecutionStates algsStates
Vector of algorithms states.
Definition EventSlot.h:84
std::vector< int > controlFlowState
State of the control flow.
Definition EventSlot.h:86