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