The Gaudi Framework  master (9f3f8895)
Loading...
Searching...
No Matches
AvalancheSchedulerSvc Class Reference

#include </builds/gaudi/Gaudi/GaudiHive/src/AvalancheSchedulerSvc.h>

Inheritance diagram for AvalancheSchedulerSvc:
Collaboration diagram for AvalancheSchedulerSvc:

Classes

struct  TaskSpec
 Struct to hold entries in the alg queues. More...
struct  AlgQueueSort
 Comparison operator to sort the queues. More...

Public Member Functions

StatusCode initialize () override
 Initialise.
StatusCode finalize () override
 Finalise.
StatusCode pushNewEvent (EventContext *eventContext) override
 Make an event available to the scheduler.
StatusCode pushNewEvents (std::vector< EventContext * > &eventContexts) override
StatusCode popFinishedEvent (EventContext *&eventContext) override
 Blocks until an event is available.
StatusCode tryPopFinishedEvent (EventContext *&eventContext) override
 Try to fetch an event from the scheduler.
unsigned int freeSlots () override
 Get free slots number.
void dumpState () override
 Dump scheduler state for all slots.
virtual StatusCode scheduleEventView (const EventContext *sourceContext, const std::string &nodeName, std::unique_ptr< EventContext > viewContext) override
 Method to inform the scheduler about event views.
virtual void recordOccupancy (int samplePeriod, std::function< void(OccupancySnapshot)> callback) override
 Sample occupancy at fixed interval (ms) Negative value to deactivate, 0 to snapshot every change Each sample, apply the callback function to the result.
bool next (TaskSpec &ts, bool asynchronous)
Public Member Functions inherited from extends< Service, IScheduler >
void const * i_cast (const InterfaceID &tid) const override
 Implementation of IInterface::i_cast.
StatusCode queryInterface (const InterfaceID &ti, void **pp) override
 Implementation of IInterface::queryInterface.
std::vector< std::string > getInterfaceNames () const override
 Implementation of IInterface::getInterfaceNames.
Public Member Functions inherited from Service
const std::string & name () const override
 Retrieve name of the service.
StatusCode configure () override
StatusCode initialize () override
StatusCode start () override
StatusCode stop () override
StatusCode finalize () override
StatusCode terminate () override
Gaudi::StateMachine::State FSMState () const override
Gaudi::StateMachine::State targetFSMState () const override
StatusCode reinitialize () override
StatusCode restart () override
StatusCode sysInitialize () override
 Initialize Service.
StatusCode sysStart () override
 Initialize Service.
StatusCode sysStop () override
 Initialize Service.
StatusCode sysFinalize () override
 Finalize Service.
StatusCode sysReinitialize () override
 Re-initialize the Service.
StatusCode sysRestart () override
 Re-initialize the Service.
 Service (std::string name, ISvcLocator *svcloc)
 Standard Constructor.
SmartIF< ISvcLocator > & serviceLocator () const override
 Retrieve pointer to service locator.
template<typename IFace = IService>
SmartIF< IFace > service (const std::string &name, bool createIf=true) const
template<class T>
Gaudi::Details::PropertyBase * declareProperty (const std::string &name, ToolHandle< T > &hndl, const std::string &doc="none")
template<class T>
StatusCode declareTool (ToolHandle< T > &handle, bool createIf=true)
template<class T>
StatusCode declareTool (ToolHandle< T > &handle, const std::string &toolTypeAndName, bool createIf=true)
 Declare used tool.
template<class T>
Gaudi::Details::PropertyBase * declareProperty (const std::string &name, ToolHandleArray< T > &hndlArr, const std::string &doc="none")
template<class T>
void addToolsArray (ToolHandleArray< T > &hndlArr)
const std::vector< IAlgTool * > & tools () const
SmartIF< IAuditorSvc > & auditorSvc () const
 The standard auditor service.May not be invoked before sysInitialize() has been invoked.
Public Member Functions inherited from PropertyHolder< CommonMessaging< implements< IService, IProperty, IStateful > > >
StatusCode setProperty (const Gaudi::Details::PropertyBase &p)
 Set the property from a property.
StatusCode setProperty (const std::string &name, const char *v)
 Special case for string literals.
StatusCode setProperty (const std::string &name, const std::string &v)
 Special case for std::string.
StatusCode setProperty (const std::string &name, const TYPE &value)
 set the property form the value
 PropertyHolder ()=default
Gaudi::Details::PropertyBase & declareProperty (Gaudi::Details::PropertyBase &prop)
 Declare a property.
Gaudi::Details::PropertyBase * declareRemoteProperty (const std::string &name, IProperty *rsvc, const std::string &rname="")
 Declare a remote property.
StatusCode setPropertyRepr (const std::string &n, const std::string &r) override
 set the property from name and value string representation
StatusCode getProperty (Gaudi::Details::PropertyBase *p) const override
 get the property
const std::vector< Gaudi::Details::PropertyBase * > & getProperties () const override
 get all properties
bool hasProperty (std::string_view name) const override
 Return true if we have a property with the given name.
Gaudi::Details::PropertyBase * property (std::string_view name) const
 \fixme property and bindPropertiesTo should be protected
void bindPropertiesTo (Gaudi::Interfaces::IOptionsSvc &optsSvc)
PropertyHolder & operator= (const PropertyHolder &)=delete
Public Member Functions inherited from CommonMessagingBase
virtual ~CommonMessagingBase ()=default
 Virtual destructor.
const SmartIF< IMessageSvc > & msgSvc () const
 The standard message service.
MsgStream & msgStream () const
 Return an uninitialized MsgStream.
MsgStream & msgStream (const MSG::Level level) const
 Predefined configurable message stream for the efficient printouts.
MsgStream & always () const
 shortcut for the method msgStream(MSG::ALWAYS)
MsgStream & fatal () const
 shortcut for the method msgStream(MSG::FATAL)
MsgStream & err () const
 shortcut for the method msgStream(MSG::ERROR)
MsgStream & error () const
 shortcut for the method msgStream(MSG::ERROR)
MsgStream & warning () const
 shortcut for the method msgStream(MSG::WARNING)
MsgStream & info () const
 shortcut for the method msgStream(MSG::INFO)
MsgStream & debug () const
 shortcut for the method msgStream(MSG::DEBUG)
MsgStream & verbose () const
 shortcut for the method msgStream(MSG::VERBOSE)
MsgStream & msg () const
 shortcut for the method msgStream(MSG::INFO)
MSG::Level msgLevel () const
 get the cached level (originally extracted from the embedded MsgStream)
bool msgLevel (MSG::Level lvl) const
 get the output level from the embedded MsgStream

Private Types

enum  ActivationState { INACTIVE = 0 , ACTIVE = 1 , FAILURE = 2 }
using AState = AlgsExecutionStates::State
using action = std::function<StatusCode()>

Private Member Functions

StatusCode dumpDataDepsGraphFile (const std::map< std::string, DataObjIDColl > &inDeps, const std::map< std::string, DataObjIDColl > &outDeps) const
void activate ()
 Activate scheduler.
StatusCode deactivate ()
 Deactivate scheduler.
unsigned int algname2index (const std::string &algoname)
 Convert a name to an integer.
const std::string & index2algname (unsigned int index)
 Convert an integer to a name.
StatusCode iterate ()
 Loop on all slots to schedule DATAREADY algorithms and sign off ready events.
StatusCode revise (unsigned int iAlgo, EventContext *contextPtr, AState state, bool iterate=false)
StatusCode schedule (TaskSpec &&)
StatusCode signoff (const TaskSpec &)
 The call to this method is triggered only from within the AlgTask.
bool isStalled (const EventSlot &) const
 Check if scheduling in a particular slot is in a stall.
void eventFailed (EventContext *eventContext)
 Method to execute if an event failed.
void dumpSchedulerState (int iSlot)
 Dump the state of the scheduler.

Private Attributes

std::chrono::duration< int64_t, std::milli > m_snapshotInterval = std::chrono::duration<int64_t, std::milli>::min()
std::chrono::system_clock::time_point m_lastSnapshot = std::chrono::system_clock::now()
std::function< void(OccupancySnapshot)> m_snapshotCallback
Gaudi::Property< int > m_threadPoolSize
Gaudi::Property< int > m_maxParallelismExtra
Gaudi::Property< std::string > m_whiteboardSvcName { this, "WhiteboardSvc", "EventDataSvc", "The whiteboard name" }
Gaudi::Property< bool > m_simulateExecution
Gaudi::Property< std::string > m_optimizationMode
Gaudi::Property< bool > m_dumpIntraEventDynamics
Gaudi::Property< int > m_numOffloadThreads
Gaudi::Property< std::size_t > m_fiberStackSize
Gaudi::Property< bool > m_checkDeps
Gaudi::Property< bool > m_checkOutput
Gaudi::Property< std::vector< std::string > > m_checkOutputIgnoreList
Gaudi::Property< std::string > m_useDataLoader
Gaudi::Property< bool > m_enableCondSvc { this, "EnableConditions", false, "Enable ConditionsSvc" }
Gaudi::Property< bool > m_showDataDeps
Gaudi::Property< bool > m_showDataFlow
Gaudi::Property< bool > m_showControlFlow
Gaudi::Property< bool > m_verboseSubSlots { this, "VerboseSubSlots", false, "Dump algorithm states for all sub-slots" }
Gaudi::Property< std::string > m_dataDepsGraphFile
Gaudi::Property< std::string > m_dataDepsGraphAlgoPattern
Gaudi::Property< std::string > m_dataDepsGraphObjectPattern
std::atomic< ActivationState > m_isActive { INACTIVE }
 Flag to track if the scheduler is active or not.
std::thread m_thread
 The thread in which the activate function runs.
std::unordered_map< std::string, unsigned int > m_algname_index_map
 Map to bookkeep the information necessary to the name2index conversion.
std::vector< std::string > m_algname_vect
 Vector to bookkeep the information necessary to the index2name conversion.
SmartIF< IPrecedenceSvc > m_precSvc
 A shortcut to the Precedence Service.
SmartIF< IHiveWhiteBoard > m_whiteboard
 A shortcut to the whiteboard.
std::vector< EventSlot > m_eventSlots
 Vector of events slots.
std::atomic_int m_freeSlots { 0 }
 Atomic to account for asyncronous updates by the scheduler wrt the rest.
tbb::concurrent_bounded_queue< EventContext * > m_finishedEvents
 Queue of finished events.
SmartIF< IAlgExecStateSvc > m_algExecStateSvc
 Algorithm execution state manager.
SmartIF< ICondSvc > m_condSvc
 A shortcut to service for Conditions handling.
unsigned int m_algosInFlight = 0
 Number of algorithms presently in flight.
SmartIF< IAlgResourcePool > m_algResourcePool
 Cache for the algorithm resource pool.
tbb::concurrent_bounded_queue< action > m_actionsQueue
 Queue where closures are stored and picked for execution.
tbb::concurrent_priority_queue< TaskSpec, AlgQueueSort > m_scheduledQueue
 Queues for scheduled algorithms.
tbb::concurrent_priority_queue< TaskSpec, AlgQueueSort > m_scheduledAsynchronousQueue
std::queue< TaskSpec > m_retryQueue
std::atomic< bool > m_needsUpdate { true }
SmartIF< IThreadPoolSvc > m_threadPoolSvc
tbb::task_arena * m_arena { nullptr }
std::unique_ptr< FiberManager > m_fiberManager { nullptr }
size_t m_maxEventsInFlight { 0 }

Friends

class AlgTask

Additional Inherited Members

Public Types inherited from extends< Service, IScheduler >
using base_class
 Typedef to this class.
using extend_interfaces_base
 Typedef to the base of this class.
Public Types inherited from Service
using Factory = Gaudi::PluginService::Factory<IService*( const std::string&, ISvcLocator* )>
Public Types inherited from PropertyHolder< CommonMessaging< implements< IService, IProperty, IStateful > > >
using PropertyHolderImpl
 Typedef used to refer to this class from derived classes, as in.
Public Types inherited from CommonMessaging< implements< IService, IProperty, IStateful > >
using base_class
Public Types inherited from extend_interfaces< Interfaces... >
using ext_iids
 take union of the ext_iids of all Interfaces...
Protected Member Functions inherited from Service
std::vector< IAlgTool * > & tools ()
 ~Service () override
int outputLevel () const
 get the Service's output level
Protected Member Functions inherited from CommonMessaging< implements< IService, IProperty, IStateful > >
MSG::Level setUpMessaging () const override final
 Set up local caches.
MSG::Level resetMessaging ()
 Reinitialize internal states.
void updateMsgStreamOutputLevel (int level)
 Update the output level of the cached MsgStream.
Protected Attributes inherited from Service
Gaudi::StateMachine::State m_state = Gaudi::StateMachine::OFFLINE
 Service state.
Gaudi::StateMachine::State m_targetState = Gaudi::StateMachine::OFFLINE
 Service state.
Gaudi::Property< int > m_outputLevel { this, "OutputLevel", MSG::NIL, "output level" }
 flag indicating whether ToolHandle tools have been added to m_tools
Gaudi::Property< bool > m_auditorInitialize { this, "AuditInitialize", false, "trigger auditor on initialize()" }
Gaudi::Property< bool > m_auditorStart { this, "AuditStart", false, "trigger auditor on start()" }
Gaudi::Property< bool > m_auditorStop { this, "AuditStop", false, "trigger auditor on stop()" }
Gaudi::Property< bool > m_auditorFinalize { this, "AuditFinalize", false, "trigger auditor on finalize()" }
Gaudi::Property< bool > m_auditorReinitialize { this, "AuditReinitialize", false, "trigger auditor on reinitialize()" }
Gaudi::Property< bool > m_auditorRestart { this, "AuditRestart", false, "trigger auditor on restart()" }
Gaudi::Property< bool > m_autoRetrieveTools
Gaudi::Property< bool > m_checkToolDeps
SmartIF< IAuditorSvc > m_pAuditorSvc
 Auditor Service.

Detailed Description

Introduction

The scheduler is named after its ability to generically maximize the average intra-event task occupancy by inducing avalanche-like concurrency disclosure waves in conditions of arbitrary intra-event task precedence constraints (see section 3.2 of http://cern.ch/go/7Jn7).

Task precedence management

The scheduler is driven by graph-based task precedence management. When compared to approach used in the ForwardSchedulerSvc, the following advantages can be emphasized:

(1) Faster decision making (thus lower concurrency disclosure downtime); (2) Capacity for proactive task scheduling decision making.

Point (2) allowed to implement a number of generic, non-intrusive intra-event throughput maximization scheduling strategies.

Scheduling principles

o Task scheduling prerequisites

A task is scheduled ASA all following conditions are met:

  • if a control flow (CF) graph traversal reaches the task;
  • when all data flow (DF) dependencies of the task are satisfied;
  • when the DF-ready task pool parsing mechanism (*) considers it, and:
    • a free (or re-entrant) algorithm instance to run within the task is available;
    • there is a free computational resource to run the task.

o (*) Avalanche induction strategies

The scheduler is able to maximize the intra-event throughput by applying several search strategies within the pool, prioritizing tasks according to the following types of precedence rules graph asymmetries:

(A) Local task-to-data asymmetry; (B) Local task-to-task asymmetry; (C) Global task-to-task asymmetry.

o Other mechanisms of throughput maximization

The scheduler supports cooperative scheduling with fibers, allowing a task to suspend its execution while waiting for an off-CPU operation to complete. The worker thread is then free to execute other work and resumes the suspended task once the operation completes.

The mechanism can be applied to the following types of tasks:

  • I/O-bound tasks;
  • tasks with computation offloading (accelerators, GPGPUs, clouds);
  • synchronization-bound tasks.

Credits

Historically, the AvalancheSchedulerSvc branched off the ForwardSchedulerSvc and in many ways built its success on ideas and code of the latter.

Author
Illya Shapoval
Version
1.0

Definition at line 114 of file AvalancheSchedulerSvc.h.

Member Typedef Documentation

◆ action

using AvalancheSchedulerSvc::action = std::function<StatusCode()>
private

Definition at line 161 of file AvalancheSchedulerSvc.h.

◆ AState

Member Enumeration Documentation

◆ ActivationState

Enumerator
INACTIVE 
ACTIVE 
FAILURE 

Definition at line 163 of file AvalancheSchedulerSvc.h.

Member Function Documentation

◆ activate()

void AvalancheSchedulerSvc::activate ( )
private

Activate scheduler.

Activate the scheduler.

From this moment on the queue of actions is checked. The checking will stop when the m_isActive flag is false and the queue is not empty. This will guarantee that all actions are executed and a stall is not created. The TBB pool must be initialised in the thread from where the tasks are launched (http://threadingbuildingblocks.org/docs/doxygen/a00342.html) The scheduler is initialised here since this method runs in a separate thread and spawns the tasks (through the execution of the lambdas)

Definition at line 467 of file AvalancheSchedulerSvc.cpp.

467 {
468
469 ON_DEBUG debug() << "AvalancheSchedulerSvc::activate()" << endmsg;
470
471 if ( m_threadPoolSvc->initPool( m_threadPoolSize, m_maxParallelismExtra ).isFailure() ) {
472 error() << "problems initializing ThreadPoolSvc" << endmsg;
474 return;
475 }
476
477 // Wait for actions pushed into the queue by finishing tasks.
478 action thisAction;
479 StatusCode sc( StatusCode::SUCCESS );
480
482
483 // Continue to wait if the scheduler is running or there is something to do
484 ON_DEBUG debug() << "Start checking the actionsQueue" << endmsg;
485 while ( m_isActive == ACTIVE || m_actionsQueue.size() != 0 ) {
486 m_actionsQueue.pop( thisAction );
487 sc = thisAction();
488 ON_VERBOSE {
489 if ( sc.isFailure() )
490 verbose() << "Action did not succeed (which is not bad per se)." << endmsg;
491 else
492 verbose() << "Action succeeded." << endmsg;
493 }
494 else sc.ignore();
495
496 // If all queued actions have been processed, update the slot states
497 if ( m_needsUpdate.load() && m_actionsQueue.empty() ) {
498 sc = iterate();
499 ON_VERBOSE {
500 if ( sc.isFailure() )
501 verbose() << "Iteration did not succeed (which is not bad per se)." << endmsg;
502 else
503 verbose() << "Iteration succeeded." << endmsg;
504 }
505 else sc.ignore();
506 }
507 }
508
509 ON_DEBUG debug() << "Terminating thread-pool resources" << endmsg;
510 if ( m_threadPoolSvc->terminatePool().isFailure() ) {
511 error() << "Problems terminating thread pool" << endmsg;
513 }
514}
#define ON_VERBOSE
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition MsgStream.h:198
#define ON_DEBUG
SmartIF< IThreadPoolSvc > m_threadPoolSvc
Gaudi::Property< int > m_threadPoolSize
tbb::concurrent_bounded_queue< action > m_actionsQueue
Queue where closures are stored and picked for execution.
std::atomic< bool > m_needsUpdate
Gaudi::Property< int > m_maxParallelismExtra
std::function< StatusCode()> action
std::atomic< ActivationState > m_isActive
Flag to track if the scheduler is active or not.
StatusCode iterate()
Loop on all slots to schedule DATAREADY algorithms and sign off ready events.
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
MsgStream & verbose() const
shortcut for the method msgStream(MSG::VERBOSE)
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
constexpr static const auto SUCCESS
Definition StatusCode.h:99

◆ algname2index()

unsigned int AvalancheSchedulerSvc::algname2index ( const std::string & algoname)
inlineprivate

Convert a name to an integer.

Definition at line 250 of file AvalancheSchedulerSvc.h.

250{ return m_algname_index_map[algoname]; }
std::unordered_map< std::string, unsigned int > m_algname_index_map
Map to bookkeep the information necessary to the name2index conversion.

◆ deactivate()

StatusCode AvalancheSchedulerSvc::deactivate ( )
private

Deactivate scheduler.

Deactivates the scheduler.

Two actions are pushed into the queue: 1) Drain the scheduler until all events are finished. 2) Flip the status flag m_isActive to false This second action is the last one to be executed by the scheduler.

Definition at line 524 of file AvalancheSchedulerSvc.cpp.

524 {
525
526 if ( m_isActive == ACTIVE ) {
527
528 // Set the number of slots available to an error code
529 m_freeSlots.store( 0 );
530
531 // Empty queue
532 action thisAction;
533 while ( m_actionsQueue.try_pop( thisAction ) ) {};
534
535 // This would be the last action
536 m_actionsQueue.push( [this]() -> StatusCode {
537 ON_VERBOSE verbose() << "Deactivating scheduler" << endmsg;
539 return StatusCode::SUCCESS;
540 } );
541 }
542
543 return StatusCode::SUCCESS;
544}
std::atomic_int m_freeSlots
Atomic to account for asyncronous updates by the scheduler wrt the rest.

◆ dumpDataDepsGraphFile()

StatusCode AvalancheSchedulerSvc::dumpDataDepsGraphFile ( const std::map< std::string, DataObjIDColl > & inDeps,
const std::map< std::string, DataObjIDColl > & outDeps ) const
private

Definition at line 1202 of file AvalancheSchedulerSvc.cpp.

1203 {
1204 // Both maps should have the same algorithm entries
1205 assert( inDeps.size() == outDeps.size() );
1206
1207 Gaudi::Hive::Graph g{ m_dataDepsGraphFile.value() };
1208 info() << "Dumping data dependencies graph to file: " << g.fileName() << endmsg;
1209
1210 // define algs and objects
1211 std::set<std::size_t> definedObjects;
1212
1213 // Regex for selection of algs and objects
1214 std::regex algNameRegex( m_dataDepsGraphAlgoPattern.value() );
1215 std::regex objNameRegex( m_dataDepsGraphObjectPattern.value() );
1216
1217 // inDeps and outDeps should have the same entries
1218 std::size_t algoIndex = 0ul;
1219 for ( const auto& [algName, ideps] : inDeps ) {
1220 if ( not std::regex_search( algName, algNameRegex ) ) continue;
1221 std::string algIndex = "Alg_" + std::to_string( algoIndex );
1222 g.addNode( algIndex, algName );
1223
1224 // inputs
1225 for ( const auto& dep : ideps ) {
1226 if ( not std::regex_search( dep.fullKey(), objNameRegex ) ) continue;
1227
1228 const auto [itr, inserted] = definedObjects.insert( dep.hash() );
1229 std::string objIndex = "obj_" + std::to_string( dep.hash() );
1230 if ( inserted ) g.addNode( objIndex, dep.key() );
1231
1232 g.addEdge( objIndex, algIndex );
1233 } // loop on ideps
1234
1235 const auto& odeps = outDeps.at( algName );
1236 for ( const auto& dep : odeps ) {
1237 if ( not std::regex_search( dep.fullKey(), objNameRegex ) ) continue;
1238
1239 const auto [itr, inserted] = definedObjects.insert( dep.hash() );
1240 std::string objIndex = "obj_" + std::to_string( dep.hash() );
1241 if ( inserted ) g.addNode( objIndex, dep.key() );
1242
1243 g.addEdge( algIndex, objIndex );
1244 } // loop on odeps
1245
1246 ++algoIndex;
1247 } // loop on inDeps
1248
1249 return StatusCode::SUCCESS;
1250}
Gaudi::Property< std::string > m_dataDepsGraphObjectPattern
Gaudi::Property< std::string > m_dataDepsGraphFile
Gaudi::Property< std::string > m_dataDepsGraphAlgoPattern
MsgStream & info() const
shortcut for the method msgStream(MSG::INFO)
dict g
Definition gaudirun.py:590

◆ dumpSchedulerState()

void AvalancheSchedulerSvc::dumpSchedulerState ( int iSlot)
private

Dump the state of the scheduler.

Used for debugging purposes, the state of the scheduler is dumped on screen in order to be inspected.

Definition at line 912 of file AvalancheSchedulerSvc.cpp.

912 {
913
914 // To have just one big message
915 std::ostringstream outputMS;
916
917 outputMS << "Dumping scheduler state\n"
918 << "=========================================================================================\n"
919 << "++++++++++++++++++++++++++++++++++++ SCHEDULER STATE ++++++++++++++++++++++++++++++++++++\n"
920 << "=========================================================================================\n\n";
921
922 //===========================================================================
923
924 outputMS << "------------------ Last schedule: Task/Event/Slot/Thread/State Mapping "
925 << "------------------\n\n";
926
927 // Figure if TimelineSvc is available (used below to detect threads IDs)
928 auto timelineSvc = serviceLocator()->service<ITimelineSvc>( "TimelineSvc", false );
929 if ( !timelineSvc.isValid() || !timelineSvc->isEnabled() ) {
930 outputMS << "WARNING Enable TimelineSvc in record mode (RecordTimeline = True) to trace the mapping\n";
931 } else {
932
933 // Figure optimal printout layout
934 size_t indt( 0 );
935 for ( auto& slot : m_eventSlots ) {
936
937 const auto& schedAlgs = slot.algsStates.algsInState( AState::SCHEDULED );
938 for ( uint algIndex : schedAlgs ) {
939 if ( index2algname( algIndex ).length() > indt ) indt = index2algname( algIndex ).length();
940 }
941 }
942
943 // Figure the last running schedule across all slots
944 for ( auto& slot : m_eventSlots ) {
945
946 const auto& schedAlgs = slot.algsStates.algsInState( AState::SCHEDULED );
947 for ( uint algIndex : schedAlgs ) {
948
949 const std::string& algoName{ index2algname( algIndex ) };
950
951 outputMS << " task: " << std::setw( indt ) << algoName << " evt/slot: " << slot.eventContext->evt() << "/"
952 << slot.eventContext->slot();
953
954 // Try to get POSIX threads IDs the currently running tasks are scheduled to
955 if ( timelineSvc.isValid() ) {
956 TimelineEvent te{};
957 te.algorithm = algoName;
958 te.slot = slot.eventContext->slot();
959 te.event = slot.eventContext->evt();
960
961 if ( timelineSvc->getLastMatching( te ) )
962 outputMS << " thread.id: 0x" << std::hex << te.thread << std::dec;
963 else
964 outputMS << " thread.id: [unknown]"; // this means a task has just
965 // been signed off as SCHEDULED,
966 // but has not been assigned to a thread yet
967 // (i.e., not running yet)
968 }
969 outputMS << " state: [" << m_algExecStateSvc->algExecState( algoName, *( slot.eventContext ) ) << "]\n";
970 }
971 }
972 }
973
974 //===========================================================================
975
976 outputMS << "\n---------------------------- Task/CF/FSM Mapping "
977 << ( 0 > iSlot ? "[all slots] --" : "[target slot] " ) << "--------------------------\n\n";
978
979 int slotCount = -1;
980 bool wasAlgError = ( iSlot >= 0 ) ? m_eventSlots[iSlot].algsStates.containsAny( { AState::ERROR } ) ||
981 subSlotAlgsInStates( m_eventSlots[iSlot], { AState::ERROR } )
982 : false;
983
984 for ( auto& slot : m_eventSlots ) {
985 ++slotCount;
986 if ( slot.complete ) continue;
987
988 outputMS << "[ slot: "
989 << ( slot.eventContext->valid() ? std::to_string( slot.eventContext->slot() ) : "[ctx invalid]" )
990 << ", event: "
991 << ( slot.eventContext->valid() ? std::to_string( slot.eventContext->evt() ) : "[ctx invalid]" );
992
993 if ( slot.eventContext->eventID().isValid() ) { outputMS << ", eventID: " << slot.eventContext->eventID(); }
994 outputMS << " ]:\n\n";
995
996 if ( 0 > iSlot || iSlot == slotCount ) {
997
998 // If an alg has thrown an error then it's not a failure of the CF/DF graph
999 if ( wasAlgError ) {
1000 outputMS << "ERROR alg(s):";
1001 int errorCount = 0;
1002 const auto& errorAlgs = slot.algsStates.algsInState( AState::ERROR );
1003 for ( uint algIndex : errorAlgs ) {
1004 outputMS << " " << index2algname( algIndex );
1005 ++errorCount;
1006 }
1007 if ( errorCount == 0 ) outputMS << " in subslot(s)";
1008 outputMS << "\n\n";
1009 } else {
1010 // Snapshot of the Control Flow and FSM states
1011 outputMS << m_precSvc->printState( slot ) << "\n";
1012 }
1013
1014 // Mention sub slots (this is expensive if the number of sub-slots is high)
1015 if ( m_verboseSubSlots && !slot.allSubSlots.empty() ) {
1016 outputMS << "\nNumber of sub-slots: " << slot.allSubSlots.size() << "\n\n";
1017 auto slotID = slot.eventContext->valid() ? std::to_string( slot.eventContext->slot() ) : "[ctx invalid]";
1018 for ( auto& ss : slot.allSubSlots ) {
1019 outputMS << "[ slot: " << slotID << ", sub-slot: "
1020 << ( ss.eventContext->valid() ? std::to_string( ss.eventContext->subSlot() ) : "[ctx invalid]" )
1021 << ", entry: " << ss.entryPoint << ", event: "
1022 << ( ss.eventContext->valid() ? std::to_string( ss.eventContext->evt() ) : "[ctx invalid]" )
1023 << " ]:\n\n";
1024 if ( wasAlgError ) {
1025 outputMS << "ERROR alg(s):";
1026 const auto& errorAlgs = ss.algsStates.algsInState( AState::ERROR );
1027 for ( uint algIndex : errorAlgs ) { outputMS << " " << index2algname( algIndex ); }
1028 outputMS << "\n\n";
1029 } else {
1030 // Snapshot of the Control Flow and FSM states in sub slot
1031 outputMS << m_precSvc->printState( ss ) << "\n";
1032 }
1033 }
1034 }
1035 }
1036 }
1037
1038 //===========================================================================
1039
1040 if ( 0 <= iSlot && !wasAlgError ) {
1041 outputMS << "\n------------------------------ Algorithm Execution States -----------------------------\n\n";
1042 m_algExecStateSvc->dump( outputMS, *( m_eventSlots[iSlot].eventContext ) );
1043 }
1044
1045 outputMS << "\n=========================================================================================\n"
1046 << "++++++++++++++++++++++++++++++++++++++ END OF DUMP ++++++++++++++++++++++++++++++++++++++\n"
1047 << "=========================================================================================\n\n";
1048
1049 info() << outputMS.str() << endmsg;
1050}
Gaudi::Property< bool > m_verboseSubSlots
SmartIF< IAlgExecStateSvc > m_algExecStateSvc
Algorithm execution state manager.
SmartIF< IPrecedenceSvc > m_precSvc
A shortcut to the Precedence Service.
const std::string & index2algname(unsigned int index)
Convert an integer to a name.
std::vector< EventSlot > m_eventSlots
Vector of events slots.
virtual SmartIF< IService > & service(const Gaudi::Utils::TypeNameString &typeName, const bool createIf=true)=0
Returns a smart pointer to a service.
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition Service.cpp:336
std::string algorithm
pthread_t thread

◆ dumpState()

void AvalancheSchedulerSvc::dumpState ( )
override

Dump scheduler state for all slots.

Definition at line 628 of file AvalancheSchedulerSvc.cpp.

628 {
629 // The state of the event slots is only modified by the scheduler thread (through the actions in
630 // m_actionsQueue), so, unless we are already on it or it is not running, let it produce the dump
631 // instead of reading the slots concurrently from the calling thread (typically the EventWatchdog).
632 if ( m_isActive != ACTIVE || std::this_thread::get_id() == m_thread.get_id() ) {
633 dumpSchedulerState( -1 );
634 return;
635 }
636
637 auto done = std::make_shared<std::promise<void>>();
638 auto future = done->get_future();
639 m_actionsQueue.push( [this, done]() -> StatusCode {
640 dumpSchedulerState( -1 );
641 done->set_value();
642 return StatusCode::SUCCESS;
643 } );
644
645 // Do not block the caller for ever if the scheduler thread is stuck: the dump stays queued
646 // and is produced as soon as the scheduler thread gets to it.
647 if ( future.wait_for( s_dumpStateTimeout ) != std::future_status::ready ) {
648 warning() << "The scheduler thread did not produce the state dump within " << s_dumpStateTimeout.count()
649 << "s (it may be blocked): the dump will appear when it proceeds" << endmsg;
650 }
651}
void dumpSchedulerState(int iSlot)
Dump the state of the scheduler.
std::thread m_thread
The thread in which the activate function runs.
MsgStream & warning() const
shortcut for the method msgStream(MSG::WARNING)

◆ eventFailed()

void AvalancheSchedulerSvc::eventFailed ( EventContext * eventContext)
private

Method to execute if an event failed.

It can be possible that an event fails.

In this case this method is called. It dumps the state of the scheduler and marks the event as finished.

Definition at line 891 of file AvalancheSchedulerSvc.cpp.

891 {
892 const uint slotIdx = eventContext->slot();
893
894 error() << "Event " << eventContext->evt() << " on slot " << slotIdx << " failed" << endmsg;
895
896 dumpSchedulerState( msgLevel( MSG::VERBOSE ) ? -1 : slotIdx );
897
898 // dump temporal and topological precedence analysis (if enabled in the PrecedenceSvc)
899 m_precSvc->dumpPrecedenceRules( m_eventSlots[slotIdx] );
900
901 // Push into the finished events queue the failed context
902 m_eventSlots[slotIdx].complete = true;
903 m_finishedEvents.push( m_eventSlots[slotIdx].eventContext.release() );
904}
tbb::concurrent_bounded_queue< EventContext * > m_finishedEvents
Queue of finished events.
MSG::Level msgLevel() const
get the cached level (originally extracted from the embedded MsgStream)
ContextEvt_t evt() const
ContextID_t slot() const
@ VERBOSE
Definition IMessageSvc.h:22

◆ finalize()

StatusCode AvalancheSchedulerSvc::finalize ( )
override

Finalise.

Here the scheduler is deactivated and the thread joined.

Definition at line 433 of file AvalancheSchedulerSvc.cpp.

433 {
434
435 StatusCode sc( Service::finalize() );
436 if ( sc.isFailure() ) warning() << "Base class could not be finalized" << endmsg;
437
438 sc = deactivate();
439 if ( sc.isFailure() ) warning() << "Scheduler could not be deactivated" << endmsg;
440
441 debug() << "Deleting FiberManager" << endmsg;
442 m_fiberManager.reset();
443
444 info() << "Joining Scheduler thread" << endmsg;
445 m_thread.join();
446
447 // Final error check after thread pool termination
448 if ( m_isActive == FAILURE ) {
449 error() << "problems in scheduler thread" << endmsg;
450 return StatusCode::FAILURE;
451 }
452
453 return sc;
454}
StatusCode deactivate()
Deactivate scheduler.
std::unique_ptr< FiberManager > m_fiberManager
StatusCode finalize() override
Definition Service.cpp:223
constexpr static const auto FAILURE
Definition StatusCode.h:100

◆ freeSlots()

unsigned int AvalancheSchedulerSvc::freeSlots ( )
override

Get free slots number.

Definition at line 624 of file AvalancheSchedulerSvc.cpp.

624{ return std::max( m_freeSlots.load(), 0 ); }

◆ index2algname()

const std::string & AvalancheSchedulerSvc::index2algname ( unsigned int index)
inlineprivate

Convert an integer to a name.

Definition at line 256 of file AvalancheSchedulerSvc.h.

256{ return m_algname_vect[index]; }
std::vector< std::string > m_algname_vect
Vector to bookkeep the information necessary to the index2name conversion.
size_t index(const Gaudi::ParticleProperty *property, const Gaudi::Interfaces::IParticlePropertySvc *service)
helper utility for mapping of Gaudi::ParticleProperty object into non-negative integral sequential id...

◆ initialize()

StatusCode AvalancheSchedulerSvc::initialize ( )
override

Initialise.

Here, among some "bureaucracy" operations, the scheduler is activated, executing the activate() function in a new thread.

In addition the algorithms list is acquired from the algResourcePool.

Definition at line 83 of file AvalancheSchedulerSvc.cpp.

83 {
84
85 // Initialise mother class (read properties, ...)
86 StatusCode sc( Service::initialize() );
87 if ( sc.isFailure() ) warning() << "Base class could not be initialized" << endmsg;
88
89 // Get hold of the TBBSvc. This should initialize the thread pool
90 m_threadPoolSvc = serviceLocator()->service( "ThreadPoolSvc" );
91 if ( !m_threadPoolSvc.isValid() ) {
92 fatal() << "Error retrieving ThreadPoolSvc" << endmsg;
94 }
95 auto castTPS = dynamic_cast<ThreadPoolSvc*>( m_threadPoolSvc.get() );
96 if ( !castTPS ) {
97 fatal() << "Cannot cast ThreadPoolSvc" << endmsg;
99 }
100 m_arena = castTPS->getArena();
101 if ( !m_arena ) {
102 fatal() << "Cannot find valid TBB task_arena" << endmsg;
103 return StatusCode::FAILURE;
104 }
105
106 // Activate the scheduler in another thread.
107 info() << "Activating scheduler in a separate thread" << endmsg;
108 std::binary_semaphore fiber_manager_initalized{ 0 };
109 m_thread = std::thread( [this, &fiber_manager_initalized]() {
110 // Initialize FiberManager
111 this->m_fiberManager =
112 std::make_unique<FiberManager>( this->m_numOffloadThreads.value(), this->m_fiberStackSize.value() );
113 fiber_manager_initalized.release();
114 this->activate();
115 } );
116 // Wait for initialization to complete
117 fiber_manager_initalized.acquire();
118
119 while ( m_isActive != ACTIVE ) {
120 if ( m_isActive == FAILURE ) {
121 fatal() << "Terminating initialization" << endmsg;
122 return StatusCode::FAILURE;
123 } else {
124 ON_DEBUG debug() << "Waiting for AvalancheSchedulerSvc to activate" << endmsg;
125 sleep( 1 );
126 }
127 }
128
129 if ( m_enableCondSvc ) {
130 // Get hold of the CondSvc
131 m_condSvc = serviceLocator()->service( "CondSvc" );
132 if ( !m_condSvc.isValid() ) {
133 warning() << "No CondSvc found, or not enabled. "
134 << "Will not manage CondAlgorithms" << endmsg;
135 m_enableCondSvc = false;
136 }
137 }
138
139 // Get the algo resource pool
140 m_algResourcePool = serviceLocator()->service( "AlgResourcePool" );
141 if ( !m_algResourcePool.isValid() ) {
142 fatal() << "Error retrieving AlgoResourcePool" << endmsg;
143 return StatusCode::FAILURE;
144 }
145
146 m_algExecStateSvc = serviceLocator()->service( "AlgExecStateSvc" );
147 if ( !m_algExecStateSvc.isValid() ) {
148 fatal() << "Error retrieving AlgExecStateSvc" << endmsg;
149 return StatusCode::FAILURE;
150 }
151
152 // Get Whiteboard
154 if ( !m_whiteboard.isValid() ) {
155 fatal() << "Error retrieving EventDataSvc interface IHiveWhiteBoard." << endmsg;
156 return StatusCode::FAILURE;
157 }
158
159 // Set the MaxEventsInFlight parameters from the number of WB stores
160 m_maxEventsInFlight = m_whiteboard->getNumberOfStores();
161
162 // Set the number of free slots
164
165 // Get the list of algorithms
166 const std::list<IAlgorithm*>& algos = m_algResourcePool->getFlatAlgList();
167 const unsigned int algsNumber = algos.size();
168 if ( algsNumber != 0 ) {
169 info() << "Found " << algsNumber << " algorithms" << endmsg;
170 } else {
171 error() << "No algorithms found" << endmsg;
172 return StatusCode::FAILURE;
173 }
174
175 /* Dependencies
176 1) Look for handles in algo, if none
177 2) Assume none are required
178 */
179
180 DataObjIDColl globalInp, globalOutp;
181
182 auto hasAsynchronousAlgorithms = false;
183
184 // figure out all outputs and check for asynchronous algorithms
185 std::map<std::string, DataObjIDColl> algosOutputDependenciesMap;
186 for ( IAlgorithm* ialgoPtr : algos ) {
187 Gaudi::Algorithm* algoPtr = dynamic_cast<Gaudi::Algorithm*>( ialgoPtr );
188 if ( !algoPtr ) {
189 fatal() << "Could not convert IAlgorithm into Gaudi::Algorithm: this will result in a crash." << endmsg;
190 return StatusCode::FAILURE;
191 }
192
193 DataObjIDColl algoOutputs;
194 for ( auto id : algoPtr->outputDataObjs() ) {
195 globalOutp.insert( id );
196 algoOutputs.insert( id );
197 }
198 algosOutputDependenciesMap[algoPtr->name()] = algoOutputs;
199
200 hasAsynchronousAlgorithms = hasAsynchronousAlgorithms || algoPtr->isAsynchronous();
201 }
202
203 if ( m_numOffloadThreads <= 0 && hasAsynchronousAlgorithms ) {
204 fatal() << "Found asynchronous algorithms, but NumOffloadThreads is " << m_numOffloadThreads
205 << "; no fiber pool threads will be available to execute them." << endmsg;
206 return StatusCode::FAILURE;
207 }
208
209 std::ostringstream ostdd;
210 ostdd << "Data Dependencies for Algorithms:";
211
212 std::map<std::string, DataObjIDColl> algosInputDependenciesMap;
213 for ( IAlgorithm* ialgoPtr : algos ) {
214 Gaudi::Algorithm* algoPtr = dynamic_cast<Gaudi::Algorithm*>( ialgoPtr );
215 if ( nullptr == algoPtr ) {
216 fatal() << "Could not convert IAlgorithm into Gaudi::Algorithm for " << ialgoPtr->name()
217 << ": this will result in a crash." << endmsg;
218 return StatusCode::FAILURE;
219 }
220
221 DataObjIDColl i1, i2;
222 DHHVisitor avis( i1, i2 );
223 algoPtr->acceptDHVisitor( &avis );
224
225 ostdd << "\n " << algoPtr->name();
226
227 auto write_owners = [&avis, &ostdd]( const DataObjID& id ) {
228 auto owners = avis.owners_names_of( id );
229 if ( !owners.empty() ) { GaudiUtils::operator<<( ostdd << ' ', owners ); }
230 };
231
232 DataObjIDColl algoDependencies;
233 if ( !algoPtr->inputDataObjs().empty() || !algoPtr->outputDataObjs().empty() ) {
234 for ( const DataObjID* idp : sortedDataObjIDColl( algoPtr->inputDataObjs() ) ) {
235 DataObjID id = *idp;
236 ostdd << "\n o INPUT " << id;
237 write_owners( id );
238 algoDependencies.insert( id );
239 globalInp.insert( id );
240 }
241 for ( const DataObjID* id : sortedDataObjIDColl( algoPtr->outputDataObjs() ) ) {
242 ostdd << "\n o OUTPUT " << *id;
243 write_owners( *id );
244 if ( id->key().find( ":" ) != std::string::npos ) {
245 error() << " in Alg " << algoPtr->name() << " alternatives are NOT allowed for outputs! id: " << *id
246 << endmsg;
247 m_showDataDeps = true;
248 }
249 }
250 } else {
251 ostdd << "\n none";
252 }
253 algosInputDependenciesMap[algoPtr->name()] = algoDependencies;
254 }
255
256 if ( m_showDataDeps ) { info() << ostdd.str() << endmsg; }
257
258 // If requested, dump a graph of the data dependencies in a .dot, .md or .graphml file
259 if ( not m_dataDepsGraphFile.empty() ) {
260 if ( dumpDataDepsGraphFile( algosInputDependenciesMap, algosOutputDependenciesMap ).isFailure() ) {
261 return StatusCode::FAILURE;
262 }
263 }
264
265 // Check if we have unmet global input dependencies, and, optionally, heal them
266 // WARNING: this step must be done BEFORE the Precedence Service is initialized
267 DataObjIDColl unmetDepInp, unusedOutp;
268 if ( m_checkDeps || m_checkOutput ) {
269 std::set<std::string> requiredInputKeys;
270 for ( auto o : globalInp ) {
271 // track aliases
272 // (assuming there should be no items with different class and same key corresponding to different objects)
273 requiredInputKeys.insert( o.key() );
274 if ( globalOutp.find( o ) == globalOutp.end() ) unmetDepInp.insert( o );
275 }
276 if ( m_checkOutput ) {
277 for ( auto o : globalOutp ) {
278 if ( globalInp.find( o ) == globalInp.end() && requiredInputKeys.find( o.key() ) == requiredInputKeys.end() ) {
279 // check ignores
280 bool ignored{};
281 for ( const std::string& algoName : m_checkOutputIgnoreList ) {
282 auto it = algosOutputDependenciesMap.find( algoName );
283 if ( it != algosOutputDependenciesMap.end() ) {
284 if ( it->second.find( o ) != it->second.end() ) {
285 ignored = true;
286 break;
287 }
288 }
289 }
290 if ( !ignored ) { unusedOutp.insert( o ); }
291 }
292 }
293 }
294 }
295
296 if ( m_checkDeps ) {
297 if ( unmetDepInp.size() > 0 ) {
298
299 auto printUnmet = [&]( auto msg ) {
300 for ( const DataObjID* o : sortedDataObjIDColl( unmetDepInp ) ) {
301 msg << " o " << *o << " required by Algorithm: " << endmsg;
302
303 for ( const auto& p : algosInputDependenciesMap )
304 if ( p.second.find( *o ) != p.second.end() ) msg << " * " << p.first << endmsg;
305 }
306 };
307
308 if ( !m_useDataLoader.empty() ) {
309
310 // Find the DataLoader Alg
311 IAlgorithm* dataLoaderAlg( nullptr );
312 for ( IAlgorithm* algo : algos )
313 if ( m_useDataLoader == algo->name() ) {
314 dataLoaderAlg = algo;
315 break;
316 }
317
318 if ( dataLoaderAlg == nullptr ) {
319 fatal() << "No DataLoader Algorithm \"" << m_useDataLoader.value()
320 << "\" found, and unmet INPUT dependencies "
321 << "detected:" << endmsg;
322 printUnmet( fatal() );
323 return StatusCode::FAILURE;
324 }
325
326 info() << "Will attribute the following unmet INPUT dependencies to \"" << dataLoaderAlg->type() << "/"
327 << dataLoaderAlg->name() << "\" Algorithm" << endmsg;
328 printUnmet( info() );
329
330 // Set the property Load of DataLoader Alg
331 Gaudi::Algorithm* dataAlg = dynamic_cast<Gaudi::Algorithm*>( dataLoaderAlg );
332 if ( !dataAlg ) {
333 fatal() << "Unable to dcast DataLoader \"" << m_useDataLoader.value() << "\" IAlg to Gaudi::Algorithm"
334 << endmsg;
335 return StatusCode::FAILURE;
336 }
337
338 for ( auto& id : unmetDepInp ) {
339 ON_DEBUG debug() << "adding OUTPUT dep \"" << id << "\" to " << dataLoaderAlg->type() << "/"
340 << dataLoaderAlg->name() << endmsg;
342 }
343
344 } else {
345 fatal() << "Auto DataLoading not requested, "
346 << "and the following unmet INPUT dependencies were found:" << endmsg;
347 printUnmet( fatal() );
348 return StatusCode::FAILURE;
349 }
350
351 } else {
352 info() << "No unmet INPUT data dependencies were found" << endmsg;
353 }
354 }
355
356 if ( m_checkOutput ) {
357 if ( unusedOutp.size() > 0 ) {
358
359 auto printUnusedOutp = [&]( auto msg ) {
360 for ( const DataObjID* o : sortedDataObjIDColl( unusedOutp ) ) {
361 msg << " o " << *o << " produced by Algorithm: " << endmsg;
362
363 for ( const auto& p : algosOutputDependenciesMap )
364 if ( p.second.find( *o ) != p.second.end() ) msg << " * " << p.first << endmsg;
365 }
366 };
367
368 fatal() << "The following unused OUTPUT items were found:" << endmsg;
369 printUnusedOutp( fatal() );
370 return StatusCode::FAILURE;
371 } else {
372 info() << "No unused OUTPUT items were found" << endmsg;
373 }
374 }
375
376 // Get the precedence service
377 m_precSvc = serviceLocator()->service( "PrecedenceSvc" );
378 if ( !m_precSvc.isValid() ) {
379 fatal() << "Error retrieving PrecedenceSvc" << endmsg;
380 return StatusCode::FAILURE;
381 }
382 const PrecedenceSvc* precSvc = dynamic_cast<const PrecedenceSvc*>( m_precSvc.get() );
383 if ( !precSvc ) {
384 fatal() << "Unable to dcast PrecedenceSvc" << endmsg;
385 return StatusCode::FAILURE;
386 }
387
388 // Fill the containers to convert algo names to index
389 m_algname_vect.resize( algsNumber );
390 for ( IAlgorithm* algo : algos ) {
391 const std::string& name = algo->name();
392 auto index = precSvc->getRules()->getAlgorithmNode( name )->getAlgoIndex();
394 m_algname_vect.at( index ) = name;
395 }
396
397 // Shortcut for the message service
398 SmartIF<IMessageSvc> messageSvc( serviceLocator() );
399 if ( !messageSvc.isValid() ) error() << "Error retrieving MessageSvc interface IMessageSvc." << endmsg;
400
402 for ( size_t i = 0; i < m_maxEventsInFlight; ++i ) {
403 m_eventSlots.emplace_back( algsNumber, precSvc->getRules()->getControlFlowNodeCounter(), messageSvc );
404 m_eventSlots.back().complete = true;
405 }
406
407 // Clearly inform about the level of concurrency
408 info() << "Concurrency level information:" << endmsg;
409 info() << " o Number of events in flight: " << m_maxEventsInFlight << endmsg;
410 info() << " o TBB thread pool size: " << m_threadPoolSize << endmsg;
411 info() << " o Fiber thread pool size: " << m_numOffloadThreads << endmsg;
412
413 // Inform about task scheduling prescriptions
414 info() << "Task scheduling settings:" << endmsg;
415 info() << " o Avalanche generation mode: "
416 << ( m_optimizationMode.empty() ? "disabled" : m_optimizationMode.toString() ) << endmsg;
417 info() << " o Scheduling of condition tasks: " << ( m_enableCondSvc ? "enabled" : "disabled" ) << endmsg;
418
419 if ( m_showControlFlow ) m_precSvc->dumpControlFlow();
420
421 if ( m_showDataFlow ) m_precSvc->dumpDataFlow();
422
423 // Simulate execution flow
424 if ( m_simulateExecution ) sc = m_precSvc->simulate( m_eventSlots[0] );
425
426 return sc;
427}
std::unordered_set< DataObjID, DataObjID_Hasher > DataObjIDColl
Definition DataObjID.h:121
Gaudi::Property< std::vector< std::string > > m_checkOutputIgnoreList
Gaudi::Property< std::string > m_useDataLoader
void activate()
Activate scheduler.
Gaudi::Property< std::string > m_optimizationMode
SmartIF< ICondSvc > m_condSvc
A shortcut to service for Conditions handling.
Gaudi::Property< bool > m_showDataFlow
Gaudi::Property< bool > m_checkDeps
SmartIF< IAlgResourcePool > m_algResourcePool
Cache for the algorithm resource pool.
Gaudi::Property< bool > m_showControlFlow
Gaudi::Property< bool > m_simulateExecution
Gaudi::Property< std::string > m_whiteboardSvcName
Gaudi::Property< int > m_numOffloadThreads
Gaudi::Property< bool > m_checkOutput
Gaudi::Property< bool > m_enableCondSvc
StatusCode dumpDataDepsGraphFile(const std::map< std::string, DataObjIDColl > &inDeps, const std::map< std::string, DataObjIDColl > &outDeps) const
SmartIF< IHiveWhiteBoard > m_whiteboard
A shortcut to the whiteboard.
Gaudi::Property< bool > m_showDataDeps
MsgStream & fatal() const
shortcut for the method msgStream(MSG::FATAL)
MsgStream & msg() const
shortcut for the method msgStream(MSG::INFO)
const DataObjIDColl & outputDataObjs() const override
void addDependency(const DataObjID &id, const Gaudi::DataHandle::Mode &mode) override
const DataObjIDColl & inputDataObjs() const override
void acceptDHVisitor(IDataHandleVisitor *) const override
const std::string & name() const override
The identifying name of the algorithm object.
bool isAsynchronous() const
Definition Algorithm.h:376
const concurrency::PrecedenceRulesGraph * getRules() const
Precedence rules accessor.
const std::string & name() const override
Retrieve name of the service.
Definition Service.cpp:333
StatusCode initialize() override
Definition Service.cpp:118
unsigned int getAlgoIndex() const
Get algorithm index.
unsigned int getControlFlowNodeCounter() const
Get total number of control flow graph nodes.
AlgorithmNode * getAlgorithmNode(const std::string &algoName) const
Get the AlgorithmNode from by algorithm name using graph index.
std::ostream & operator<<(std::ostream &s, const std::pair< T1, T2 > &p)
Serialize an std::pair in a python like format. E.g. "(1, 2)".

◆ isStalled()

bool AvalancheSchedulerSvc::isStalled ( const EventSlot & slot) const
private

Check if scheduling in a particular slot is in a stall.

Check if we are in present of a stall condition for a particular slot.

This is the case when a slot has no actions queued in the actionsQueue, has no scheduled algorithms and has no algorithms with all of its dependencies satisfied.

Definition at line 873 of file AvalancheSchedulerSvc.cpp.

873 {
874
875 if ( !slot.algsStates.containsAny( { AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) &&
876 !subSlotAlgsInStates( slot, { AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) ) {
877
878 error() << "*** Stall detected, event context: " << slot.eventContext.get() << endmsg;
879
880 return true;
881 }
882 return false;
883}
bool containsAny(std::initializer_list< State > l) const
check if the collection contains at least one state of any listed types
std::unique_ptr< EventContext > eventContext
Cache for the eventContext.
Definition EventSlot.h:82
AlgsExecutionStates algsStates
Vector of algorithms states.
Definition EventSlot.h:84

◆ iterate()

StatusCode AvalancheSchedulerSvc::iterate ( )
private

Loop on all slots to schedule DATAREADY algorithms and sign off ready events.

Loop on all slots to schedule DATAREADY algorithms, sign off ready ones or detect execution stalls.

To check if an event is finished the method verifies that the root control flow decision of the task precedence graph is resolved and there are no algorithms moving in-between INITIAL and EVTACCEPTED FSM states.

Definition at line 698 of file AvalancheSchedulerSvc.cpp.

698 {
699
700 StatusCode global_sc( StatusCode::SUCCESS );
701
702 // Retry algorithms
703 const size_t retries = m_retryQueue.size();
704 for ( unsigned int retryIndex = 0; retryIndex < retries; ++retryIndex ) {
705 TaskSpec retryTS = std::move( m_retryQueue.front() );
706 m_retryQueue.pop();
707 global_sc = schedule( std::move( retryTS ) );
708 }
709
710 // Loop over all slots
711 OccupancySnapshot nextSnap;
712 auto now = std::chrono::system_clock::now();
713 for ( EventSlot& thisSlot : m_eventSlots ) {
714
715 // Ignore slots without a valid context (relevant when populating scheduler for first time)
716 if ( !thisSlot.eventContext ) continue;
717
718 int iSlot = thisSlot.eventContext->slot();
719
720 // Cache the states of the algorithms to improve readability and performance
721 AlgsExecutionStates& thisAlgsStates = thisSlot.algsStates;
722
723 StatusCode partial_sc = StatusCode::FAILURE;
724
725 // Make an occupancy snapshot
726 if ( m_snapshotInterval != std::chrono::duration<int64_t, std::milli>::min() &&
728
729 // Initialise snapshot
730 if ( nextSnap.states.empty() ) {
731 nextSnap.time = now;
732 nextSnap.states.resize( m_eventSlots.size() );
733 }
734
735 // Store alg states
736 std::vector<int>& slotStateTotals = nextSnap.states[iSlot];
737 slotStateTotals.resize( AState::MAXVALUE );
738 for ( uint8_t state = 0; state < AState::MAXVALUE; ++state ) {
739 slotStateTotals[state] = thisSlot.algsStates.sizeOfSubset( AState( state ) );
740 }
741
742 // Add subslot alg states
743 for ( auto& subslot : thisSlot.allSubSlots ) {
744 for ( uint8_t state = 0; state < AState::MAXVALUE; ++state ) {
745 slotStateTotals[state] += subslot.algsStates.sizeOfSubset( AState( state ) );
746 }
747 }
748 }
749
750 // Perform DR->SCHEDULED
751 const auto& drAlgs = thisAlgsStates.algsInState( AState::DATAREADY );
752 for ( uint algIndex : drAlgs ) {
753 const std::string& algName{ index2algname( algIndex ) };
754 unsigned int rank{ m_optimizationMode.empty() ? 0 : m_precSvc->getPriority( algName ) };
755 bool asynchronous{ m_precSvc->isAsynchronous( algName ) };
756
757 partial_sc =
758 schedule( TaskSpec( nullptr, algIndex, algName, rank, asynchronous, iSlot, thisSlot.eventContext.get() ) );
759
760 ON_VERBOSE if ( partial_sc.isFailure() ) verbose()
761 << "Could not apply transition from " << AState::DATAREADY << " for algorithm " << algName
762 << " on processing slot " << iSlot << endmsg;
763 }
764
765 // Check for algorithms ready in sub-slots
766 for ( auto& subslot : thisSlot.allSubSlots ) {
767 const auto& drAlgsSubSlot = subslot.algsStates.algsInState( AState::DATAREADY );
768 for ( uint algIndex : drAlgsSubSlot ) {
769 const std::string& algName{ index2algname( algIndex ) };
770 unsigned int rank{ m_optimizationMode.empty() ? 0 : m_precSvc->getPriority( algName ) };
771 bool asynchronous{ m_precSvc->isAsynchronous( algName ) };
772 partial_sc =
773 schedule( TaskSpec( nullptr, algIndex, algName, rank, asynchronous, iSlot, subslot.eventContext.get() ) );
774 }
775 }
776
778 std::stringstream s;
779 s << "START, " << thisAlgsStates.sizeOfSubset( AState::CONTROLREADY ) << ", "
780 << thisAlgsStates.sizeOfSubset( AState::DATAREADY ) << ", " << thisAlgsStates.sizeOfSubset( AState::SCHEDULED )
781 << ", " << std::chrono::high_resolution_clock::now().time_since_epoch().count() << "\n";
782 auto threads = ( m_threadPoolSize != -1 ) ? std::to_string( m_threadPoolSize )
783 : std::to_string( std::thread::hardware_concurrency() );
784 std::ofstream myfile;
785 myfile.open( "IntraEventFSMOccupancy_" + threads + "T.csv", std::ios::app );
786 myfile << s.str();
787 myfile.close();
788 }
789
790 // Not complete because this would mean that the slot is already free!
791 if ( m_precSvc->CFRulesResolved( thisSlot ) &&
792 !thisSlot.algsStates.containsAny(
793 { AState::CONTROLREADY, AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) &&
794 !subSlotAlgsInStates( thisSlot,
795 { AState::CONTROLREADY, AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) &&
796 !thisSlot.complete ) {
797
798 thisSlot.complete = true;
799 // if the event did not fail, add it to the finished events
800 // otherwise it is taken care of in the error handling
801 if ( m_algExecStateSvc->eventStatus( *thisSlot.eventContext ) == EventStatus::Success ) {
802 ON_DEBUG debug() << "Event " << thisSlot.eventContext->evt() << " finished (slot "
803 << thisSlot.eventContext->slot() << ")." << endmsg;
804 m_finishedEvents.push( thisSlot.eventContext.release() );
805 }
806
807 // now let's return the fully evaluated result of the control flow
808 ON_DEBUG debug() << m_precSvc->printState( thisSlot ) << endmsg;
809
810 thisSlot.eventContext.reset( nullptr );
811
812 } else if ( isStalled( thisSlot ) ) {
813 m_algExecStateSvc->setEventStatus( EventStatus::AlgStall, *thisSlot.eventContext );
814 eventFailed( thisSlot.eventContext.get() ); // can't release yet
815 }
816 partial_sc.ignore();
817 } // end loop on slots
818
819 // Process snapshot
820 if ( !nextSnap.states.empty() ) {
821 m_lastSnapshot = nextSnap.time;
822 m_snapshotCallback( std::move( nextSnap ) );
823 }
824
825 ON_VERBOSE verbose() << "Iteration done." << endmsg;
826 m_needsUpdate.store( false );
827 return global_sc;
828}
const boost::container::flat_set< int > algsInState(State state) const
size_t sizeOfSubset(State state) const
Gaudi::Property< bool > m_dumpIntraEventDynamics
std::chrono::system_clock::time_point m_lastSnapshot
std::function< void(OccupancySnapshot)> m_snapshotCallback
std::queue< TaskSpec > m_retryQueue
AlgsExecutionStates::State AState
bool isStalled(const EventSlot &) const
Check if scheduling in a particular slot is in a stall.
StatusCode schedule(TaskSpec &&)
std::chrono::duration< int64_t, std::milli > m_snapshotInterval
void eventFailed(EventContext *eventContext)
Method to execute if an event failed.
bool isFailure() const
Definition StatusCode.h:118
void ignore() const
Allow discarding a StatusCode without warning.
Definition StatusCode.h:128
Struct to hold entries in the alg queues.

◆ next()

bool AvalancheSchedulerSvc::next ( TaskSpec & ts,
bool asynchronous )
inline

Definition at line 371 of file AvalancheSchedulerSvc.h.

371 {
372 if ( asynchronous ) { return m_scheduledAsynchronousQueue.try_pop( ts ); }
373 return m_scheduledQueue.try_pop( ts );
374 }
tbb::concurrent_priority_queue< TaskSpec, AlgQueueSort > m_scheduledQueue
Queues for scheduled algorithms.
tbb::concurrent_priority_queue< TaskSpec, AlgQueueSort > m_scheduledAsynchronousQueue

◆ popFinishedEvent()

StatusCode AvalancheSchedulerSvc::popFinishedEvent ( EventContext *& eventContext)
override

Blocks until an event is available.

Get a finished event or block until one becomes available.

Definition at line 657 of file AvalancheSchedulerSvc.cpp.

657 {
658
659 // ON_DEBUG debug() << "popFinishedEvent: queue size: " << m_finishedEvents.size() << endmsg;
660 if ( m_freeSlots.load() == (int)m_maxEventsInFlight || m_isActive == INACTIVE ) {
661 // ON_DEBUG debug() << "freeslots: " << m_freeSlots << "/" << m_maxEventsInFlight
662 // << " active: " << m_isActive << endmsg;
663 return StatusCode::FAILURE;
664 } else {
665 // ON_DEBUG debug() << "freeslots: " << m_freeSlots << "/" << m_maxEventsInFlight
666 // << " active: " << m_isActive << endmsg;
667 m_finishedEvents.pop( eventContext );
668 ++m_freeSlots;
669 ON_DEBUG debug() << "Popped slot " << eventContext->slot() << " (event " << eventContext->evt() << ")" << endmsg;
670 return StatusCode::SUCCESS;
671 }
672}

◆ pushNewEvent()

StatusCode AvalancheSchedulerSvc::pushNewEvent ( EventContext * eventContext)
override

Make an event available to the scheduler.

Add event to the scheduler.

There are two cases possible: 1) No slot is free. A StatusCode::FAILURE is returned. 2) At least one slot is free. An action which resets the slot and kicks off its update is queued.

Definition at line 555 of file AvalancheSchedulerSvc.cpp.

555 {
556
557 if ( !eventContext ) {
558 fatal() << "Event context is nullptr" << endmsg;
559 return StatusCode::FAILURE;
560 }
561
562 if ( m_freeSlots.load() == 0 ) {
563 ON_DEBUG debug() << "A free processing slot could not be found." << endmsg;
564 return StatusCode::FAILURE;
565 }
566
567 // no problem as push new event is only called from one thread (event loop manager)
568 --m_freeSlots;
569
570 auto action = [this, eventContext]() -> StatusCode {
571 // Event processing slot forced to be the same as the wb slot
572 const unsigned int thisSlotNum = eventContext->slot();
573 EventSlot& thisSlot = m_eventSlots[thisSlotNum];
574 if ( !thisSlot.complete ) {
575 fatal() << "The slot " << thisSlotNum << " is supposed to be a finished event but it's not" << endmsg;
576 return StatusCode::FAILURE;
577 }
578
579 ON_DEBUG debug() << "Executing event " << eventContext->evt() << " on slot " << thisSlotNum << endmsg;
580 thisSlot.reset( eventContext );
581
582 // Result status code:
583 StatusCode result = StatusCode::SUCCESS;
584
585 // promote to CR and DR the initial set of algorithms
586 Cause cs = { Cause::source::Root, "RootDecisionHub" };
587 if ( m_precSvc->iterate( thisSlot, cs ).isFailure() ) {
588 error() << "Failed to call IPrecedenceSvc::iterate for slot " << thisSlotNum << endmsg;
589 result = StatusCode::FAILURE;
590 }
591
592 if ( this->iterate().isFailure() ) {
593 error() << "Failed to call AvalancheSchedulerSvc::updateStates for slot " << thisSlotNum << endmsg;
594 result = StatusCode::FAILURE;
595 }
596
597 return result;
598 }; // end of lambda
599
600 // Kick off scheduling
601 ON_VERBOSE {
602 verbose() << "Pushing the action to update the scheduler for slot " << eventContext->slot() << endmsg;
603 verbose() << "Free slots available " << m_freeSlots.load() << endmsg;
604 }
605
606 m_actionsQueue.push( action );
607
608 return StatusCode::SUCCESS;
609}
bool complete
Flags completion of the event.
Definition EventSlot.h:88
void reset(EventContext *theeventContext)
Reset all resources in order to reuse the slot (thread-unsafe).
Definition EventSlot.h:48

◆ pushNewEvents()

StatusCode AvalancheSchedulerSvc::pushNewEvents ( std::vector< EventContext * > & eventContexts)
override

Definition at line 613 of file AvalancheSchedulerSvc.cpp.

613 {
614 StatusCode sc;
615 for ( auto context : eventContexts ) {
616 sc = pushNewEvent( context );
617 if ( sc != StatusCode::SUCCESS ) return sc;
618 }
619 return sc;
620}
StatusCode pushNewEvent(EventContext *eventContext) override
Make an event available to the scheduler.

◆ recordOccupancy()

void AvalancheSchedulerSvc::recordOccupancy ( int samplePeriod,
std::function< void(OccupancySnapshot)> callback )
overridevirtual

Sample occupancy at fixed interval (ms) Negative value to deactivate, 0 to snapshot every change Each sample, apply the callback function to the result.

Definition at line 1187 of file AvalancheSchedulerSvc.cpp.

1187 {
1188
1189 auto action = [this, samplePeriod, callback = std::move( callback )]() -> StatusCode {
1190 if ( samplePeriod < 0 ) {
1191 this->m_snapshotInterval = std::chrono::duration<int64_t, std::milli>::min();
1192 } else {
1193 this->m_snapshotInterval = std::chrono::duration<int64_t, std::milli>( samplePeriod );
1194 m_snapshotCallback = std::move( callback );
1195 }
1196 return StatusCode::SUCCESS;
1197 };
1198
1199 m_actionsQueue.push( std::move( action ) );
1200}

◆ revise()

StatusCode AvalancheSchedulerSvc::revise ( unsigned int iAlgo,
EventContext * contextPtr,
AState state,
bool iterate = false )
private

Definition at line 832 of file AvalancheSchedulerSvc.cpp.

832 {
833 StatusCode sc;
834 auto slotIndex = contextPtr->slot();
835 EventSlot& slot = m_eventSlots[slotIndex];
836 Cause cs = { Cause::source::Task, index2algname( iAlgo ) };
837
838 if ( contextPtr->usesSubSlot() ) {
839 // Sub-slot
840 auto subSlotIndex = contextPtr->subSlot();
841 EventSlot& subSlot = slot.allSubSlots[subSlotIndex];
842
843 sc = subSlot.algsStates.set( iAlgo, state );
844
845 if ( sc.isSuccess() ) {
846 ON_VERBOSE verbose() << "Promoted " << index2algname( iAlgo ) << " to " << state << " [slot:" << slotIndex
847 << ", subslot:" << subSlotIndex << ", event:" << contextPtr->evt() << "]" << endmsg;
848 // Revise states of algorithms downstream the precedence graph
849 if ( iterate ) sc = m_precSvc->iterate( subSlot, cs );
850 }
851 } else {
852 // Event level (standard behaviour)
853 sc = slot.algsStates.set( iAlgo, state );
854
855 if ( sc.isSuccess() ) {
856 ON_VERBOSE verbose() << "Promoted " << index2algname( iAlgo ) << " to " << state << " [slot:" << slotIndex
857 << ", event:" << contextPtr->evt() << "]" << endmsg;
858 // Revise states of algorithms downstream the precedence graph
859 if ( iterate ) sc = m_precSvc->iterate( slot, cs );
860 }
861 }
862 return sc;
863}
StatusCode set(unsigned int iAlgo, State newState)
ContextID_t subSlot() const
bool usesSubSlot() const
bool isSuccess() const
Definition StatusCode.h:302
std::vector< EventSlot > allSubSlots
Actual sub-slot instances.
Definition EventSlot.h:99

◆ schedule()

StatusCode AvalancheSchedulerSvc::schedule ( TaskSpec && ts)
private

Definition at line 1054 of file AvalancheSchedulerSvc.cpp.

1054 {
1055
1056 // Check if a free Algorithm instance is available
1057 StatusCode getAlgSC( m_algResourcePool->acquireAlgorithm( ts.algName, ts.algPtr ) );
1058
1059 // If an instance is available, proceed to scheduling
1060 StatusCode sc;
1061 if ( getAlgSC.isSuccess() ) {
1062
1063 // Decide how to schedule the task and schedule it
1064 if ( -100 != m_threadPoolSize ) {
1065
1066 // Cache values before moving the TaskSpec further
1067 unsigned int algIndex{ ts.algIndex };
1068 std::string_view algName( ts.algName );
1069 unsigned int algRank{ ts.algRank };
1070 bool asynchronous{ ts.asynchronous };
1071 int slotIndex{ ts.slotIndex };
1072 EventContext* contextPtr{ ts.contextPtr };
1073
1074 if ( asynchronous ) {
1075 // Add to asynchronous scheduled queue
1076 m_scheduledAsynchronousQueue.push( std::move( ts ) );
1077
1078 // Schedule task
1079 m_fiberManager->schedule( AlgTask( this, serviceLocator(), m_algExecStateSvc, asynchronous ) );
1080 }
1081
1082 if ( !asynchronous ) {
1083 // Add the algorithm to the scheduled queue
1084 m_scheduledQueue.push( std::move( ts ) );
1085
1086 // Prepare a TBB task that will execute the Algorithm according to the above queued specs
1087 m_arena->enqueue( AlgTask( this, serviceLocator(), m_algExecStateSvc, asynchronous ) );
1089 }
1090 sc = revise( algIndex, contextPtr, AState::SCHEDULED );
1091
1092 ON_DEBUG debug() << "Scheduled " << algName << " [slot:" << slotIndex << ", event:" << contextPtr->evt()
1093 << ", rank:" << algRank << ", asynchronous:" << ( asynchronous ? "yes" : "no" )
1094 << "]. Scheduled algorithms: " << m_algosInFlight << endmsg;
1095
1096 } else { // Avoid scheduling via TBB if the pool size is -100. Instead, run here in the scheduler's control thread
1097 // Beojan: I don't think this bit works. ts hasn't been pushed into any queue so AlgTask won't retrieve it
1099 sc = revise( ts.algIndex, ts.contextPtr, AState::SCHEDULED );
1100 AlgTask( this, serviceLocator(), m_algExecStateSvc, ts.asynchronous )();
1102 }
1103 } else { // if no Algorithm instance available, retry later
1104
1105 sc = revise( ts.algIndex, ts.contextPtr, AState::RESOURCELESS );
1106 // Add the algorithm to the retry queue
1107 m_retryQueue.push( std::move( ts ) );
1108 }
1109
1111
1112 return sc;
1113}
StatusCode revise(unsigned int iAlgo, EventContext *contextPtr, AState state, bool iterate=false)
unsigned int m_algosInFlight
Number of algorithms presently in flight.

◆ scheduleEventView()

StatusCode AvalancheSchedulerSvc::scheduleEventView ( const EventContext * sourceContext,
const std::string & nodeName,
std::unique_ptr< EventContext > viewContext )
overridevirtual

Method to inform the scheduler about event views.

Definition at line 1147 of file AvalancheSchedulerSvc.cpp.

1148 {
1149 // Prevent view nesting
1150 if ( sourceContext->usesSubSlot() ) {
1151 fatal() << "Attempted to nest EventViews at node " << nodeName << ": this is not supported" << endmsg;
1152 return StatusCode::FAILURE;
1153 }
1154
1155 ON_VERBOSE verbose() << "Queuing a view for [" << viewContext.get() << "]" << endmsg;
1156
1157 // It's not possible to create an std::functional from a move-capturing lambda
1158 // So, we have to release the unique pointer
1159 auto action = [this, slotIndex = sourceContext->slot(), viewContextPtr = viewContext.release(),
1160 &nodeName]() -> StatusCode {
1161 // Attach the sub-slot to the top-level slot
1162 EventSlot& topSlot = this->m_eventSlots[slotIndex];
1163
1164 if ( viewContextPtr ) {
1165 // Re-create the unique pointer
1166 auto viewContext = std::unique_ptr<EventContext>( viewContextPtr );
1167 topSlot.addSubSlot( std::move( viewContext ), nodeName );
1168 return StatusCode::SUCCESS;
1169 } else {
1170 // Disable the view node if there are no views
1171 topSlot.disableSubSlots( nodeName );
1172 return StatusCode::SUCCESS;
1173 }
1174 };
1175
1176 m_actionsQueue.push( std::move( action ) );
1177
1178 return StatusCode::SUCCESS;
1179}
void addSubSlot(std::unique_ptr< EventContext > viewContext, const std::string &nodeName)
Add a subslot to the slot (this constructs a new slot and registers it with the parent one).
Definition EventSlot.h:60
void disableSubSlots(const std::string &nodeName)
Disable event views for a given CF view node by registering an empty container Contact B.
Definition EventSlot.h:77

◆ signoff()

StatusCode AvalancheSchedulerSvc::signoff ( const TaskSpec & ts)
private

The call to this method is triggered only from within the AlgTask.

Definition at line 1120 of file AvalancheSchedulerSvc.cpp.

1120 {
1121
1122 Gaudi::Hive::setCurrentContext( ts.contextPtr );
1123
1125
1126 const AlgExecStateRef algstate = m_algExecStateSvc->algExecState( ts.algPtr, *( ts.contextPtr ) );
1127 AState state = algstate.execStatus().isSuccess()
1128 ? ( algstate.filterPassed() ? AState::EVTACCEPTED : AState::EVTREJECTED )
1129 : AState::ERROR;
1130
1131 // Update algorithm state and revise the downstream states
1132 auto sc = revise( ts.algIndex, ts.contextPtr, state, true );
1133
1134 ON_DEBUG debug() << "Executed " << ts.algName << " [slot:" << ts.slotIndex << ", event:" << ts.contextPtr->evt()
1135 << ", rank:" << ts.algRank << ", asynchronous:" << ( ts.asynchronous ? "yes" : "no" )
1136 << "]. Scheduled algorithms: " << m_algosInFlight << endmsg;
1137
1138 // Prompt a call to updateStates
1139 m_needsUpdate.store( true );
1140 return sc;
1141}
const StatusCode & execStatus() const
bool filterPassed() const
GAUDI_API void setCurrentContext(const EventContext *ctx)

◆ tryPopFinishedEvent()

StatusCode AvalancheSchedulerSvc::tryPopFinishedEvent ( EventContext *& eventContext)
override

Try to fetch an event from the scheduler.

Try to get a finished event, if not available just return a failure.

Definition at line 678 of file AvalancheSchedulerSvc.cpp.

678 {
679
680 if ( m_finishedEvents.try_pop( eventContext ) ) {
681 ON_DEBUG debug() << "Try Pop successful slot " << eventContext->slot() << "(event " << eventContext->evt() << ")"
682 << endmsg;
683 ++m_freeSlots;
684 return StatusCode::SUCCESS;
685 }
686 return StatusCode::FAILURE;
687}

◆ AlgTask

friend class AlgTask
friend

Definition at line 116 of file AvalancheSchedulerSvc.h.

Member Data Documentation

◆ m_actionsQueue

tbb::concurrent_bounded_queue<action> AvalancheSchedulerSvc::m_actionsQueue
private

Queue where closures are stored and picked for execution.

Definition at line 314 of file AvalancheSchedulerSvc.h.

◆ m_algExecStateSvc

SmartIF<IAlgExecStateSvc> AvalancheSchedulerSvc::m_algExecStateSvc
private

Algorithm execution state manager.

Definition at line 277 of file AvalancheSchedulerSvc.h.

◆ m_algname_index_map

std::unordered_map<std::string, unsigned int> AvalancheSchedulerSvc::m_algname_index_map
private

Map to bookkeep the information necessary to the name2index conversion.

Definition at line 253 of file AvalancheSchedulerSvc.h.

◆ m_algname_vect

std::vector<std::string> AvalancheSchedulerSvc::m_algname_vect
private

Vector to bookkeep the information necessary to the index2name conversion.

Definition at line 259 of file AvalancheSchedulerSvc.h.

◆ m_algosInFlight

unsigned int AvalancheSchedulerSvc::m_algosInFlight = 0
private

Number of algorithms presently in flight.

Definition at line 283 of file AvalancheSchedulerSvc.h.

◆ m_algResourcePool

SmartIF<IAlgResourcePool> AvalancheSchedulerSvc::m_algResourcePool
private

Cache for the algorithm resource pool.

Definition at line 309 of file AvalancheSchedulerSvc.h.

◆ m_arena

tbb::task_arena* AvalancheSchedulerSvc::m_arena { nullptr }
private

Definition at line 364 of file AvalancheSchedulerSvc.h.

364{ nullptr };

◆ m_checkDeps

Gaudi::Property<bool> AvalancheSchedulerSvc::m_checkDeps
private
Initial value:
{ this, "CheckDependencies", false,
"Runtime check of Algorithm Input Data Dependencies" }

Definition at line 194 of file AvalancheSchedulerSvc.h.

194 { this, "CheckDependencies", false,
195 "Runtime check of Algorithm Input Data Dependencies" };

◆ m_checkOutput

Gaudi::Property<bool> AvalancheSchedulerSvc::m_checkOutput
private
Initial value:
{ this, "CheckOutputUsage", false,
"Runtime check of Algorithm Output Data usage" }

Definition at line 196 of file AvalancheSchedulerSvc.h.

196 { this, "CheckOutputUsage", false,
197 "Runtime check of Algorithm Output Data usage" };

◆ m_checkOutputIgnoreList

Gaudi::Property<std::vector<std::string> > AvalancheSchedulerSvc::m_checkOutputIgnoreList
private
Initial value:
{
this,
"CheckOutputUsageIgnoreList",
{},
"Ignore outputs of the Algorithms of this name when doing the check",
"OrderedSet<std::string>" }

Definition at line 198 of file AvalancheSchedulerSvc.h.

198 {
199 this,
200 "CheckOutputUsageIgnoreList",
201 {},
202 "Ignore outputs of the Algorithms of this name when doing the check",
203 "OrderedSet<std::string>" };

◆ m_condSvc

SmartIF<ICondSvc> AvalancheSchedulerSvc::m_condSvc
private

A shortcut to service for Conditions handling.

Definition at line 280 of file AvalancheSchedulerSvc.h.

◆ m_dataDepsGraphAlgoPattern

Gaudi::Property<std::string> AvalancheSchedulerSvc::m_dataDepsGraphAlgoPattern
private
Initial value:
{
this, "DataDepsGraphAlgPattern", ".*",
"Regex pattern for selecting desired Algorithms by name, whose data dependency has to be included in the data "
"deps graph" }

Definition at line 226 of file AvalancheSchedulerSvc.h.

226 {
227 this, "DataDepsGraphAlgPattern", ".*",
228 "Regex pattern for selecting desired Algorithms by name, whose data dependency has to be included in the data "
229 "deps graph" };

◆ m_dataDepsGraphFile

Gaudi::Property<std::string> AvalancheSchedulerSvc::m_dataDepsGraphFile
private
Initial value:
{
this, "DataDepsGraphFile", "",
"Name of the output file (.dot, .md or .graphml extensions allowed) containing the data dependency graph "
"for some selected Algorithms" }

Definition at line 221 of file AvalancheSchedulerSvc.h.

221 {
222 this, "DataDepsGraphFile", "",
223 "Name of the output file (.dot, .md or .graphml extensions allowed) containing the data dependency graph "
224 "for some selected Algorithms" };

◆ m_dataDepsGraphObjectPattern

Gaudi::Property<std::string> AvalancheSchedulerSvc::m_dataDepsGraphObjectPattern
private
Initial value:
{
this, "DataDepsGraphObjectPattern", ".*",
"Regex pattern for selecting desired input or output by their full key" }

Definition at line 231 of file AvalancheSchedulerSvc.h.

231 {
232 this, "DataDepsGraphObjectPattern", ".*",
233 "Regex pattern for selecting desired input or output by their full key" };

◆ m_dumpIntraEventDynamics

Gaudi::Property<bool> AvalancheSchedulerSvc::m_dumpIntraEventDynamics
private
Initial value:
{ this, "DumpIntraEventDynamics", false,
"Dump intra-event concurrency dynamics to csv file" }

Definition at line 185 of file AvalancheSchedulerSvc.h.

185 { this, "DumpIntraEventDynamics", false,
186 "Dump intra-event concurrency dynamics to csv file" };

◆ m_enableCondSvc

Gaudi::Property<bool> AvalancheSchedulerSvc::m_enableCondSvc { this, "EnableConditions", false, "Enable ConditionsSvc" }
private

Definition at line 208 of file AvalancheSchedulerSvc.h.

208{ this, "EnableConditions", false, "Enable ConditionsSvc" };

◆ m_eventSlots

std::vector<EventSlot> AvalancheSchedulerSvc::m_eventSlots
private

Vector of events slots.

Definition at line 268 of file AvalancheSchedulerSvc.h.

◆ m_fiberManager

std::unique_ptr<FiberManager> AvalancheSchedulerSvc::m_fiberManager { nullptr }
private

Definition at line 365 of file AvalancheSchedulerSvc.h.

365{ nullptr };

◆ m_fiberStackSize

Gaudi::Property<std::size_t> AvalancheSchedulerSvc::m_fiberStackSize
private
Initial value:
{ this, "FiberStackSize", 16ULL * 1024 * 1024,
"Stack size for fibers (asynchronous algorithms)" }

Definition at line 192 of file AvalancheSchedulerSvc.h.

192 { this, "FiberStackSize", 16ULL * 1024 * 1024,
193 "Stack size for fibers (asynchronous algorithms)" };

◆ m_finishedEvents

tbb::concurrent_bounded_queue<EventContext*> AvalancheSchedulerSvc::m_finishedEvents
private

Queue of finished events.

Definition at line 274 of file AvalancheSchedulerSvc.h.

◆ m_freeSlots

std::atomic_int AvalancheSchedulerSvc::m_freeSlots { 0 }
private

Atomic to account for asyncronous updates by the scheduler wrt the rest.

Definition at line 271 of file AvalancheSchedulerSvc.h.

271{ 0 };

◆ m_isActive

std::atomic<ActivationState> AvalancheSchedulerSvc::m_isActive { INACTIVE }
private

Flag to track if the scheduler is active or not.

Definition at line 244 of file AvalancheSchedulerSvc.h.

244{ INACTIVE };

◆ m_lastSnapshot

std::chrono::system_clock::time_point AvalancheSchedulerSvc::m_lastSnapshot = std::chrono::system_clock::now()
private

Definition at line 167 of file AvalancheSchedulerSvc.h.

◆ m_maxEventsInFlight

size_t AvalancheSchedulerSvc::m_maxEventsInFlight { 0 }
private

Definition at line 367 of file AvalancheSchedulerSvc.h.

367{ 0 };

◆ m_maxParallelismExtra

Gaudi::Property<int> AvalancheSchedulerSvc::m_maxParallelismExtra
private
Initial value:
{
this, "maxParallelismExtra", 0,
"Allows to add some extra threads to the maximum parallelism set in TBB"
"The TBB max parallelism is set as: ThreadPoolSize + maxParallelismExtra + 1" }

Definition at line 175 of file AvalancheSchedulerSvc.h.

175 {
176 this, "maxParallelismExtra", 0,
177 "Allows to add some extra threads to the maximum parallelism set in TBB"
178 "The TBB max parallelism is set as: ThreadPoolSize + maxParallelismExtra + 1" };

◆ m_needsUpdate

std::atomic<bool> AvalancheSchedulerSvc::m_needsUpdate { true }
private

Definition at line 358 of file AvalancheSchedulerSvc.h.

358{ true };

◆ m_numOffloadThreads

Gaudi::Property<int> AvalancheSchedulerSvc::m_numOffloadThreads
private
Initial value:
{
this, "NumOffloadThreads", 0,
"Number of threads to use for CPU portion of asynchronous algorithms. Asynchronous algorithms must be flagged "
"and use Boost Fiber functionality to suspend while waiting for offloaded work." }

Definition at line 187 of file AvalancheSchedulerSvc.h.

187 {
188 this, "NumOffloadThreads", 0,
189 "Number of threads to use for CPU portion of asynchronous algorithms. Asynchronous algorithms must be flagged "
190 "and use Boost Fiber functionality to suspend while waiting for offloaded work." };

◆ m_optimizationMode

Gaudi::Property<std::string> AvalancheSchedulerSvc::m_optimizationMode
private
Initial value:
{ this, "Optimizer", "",
"The following modes are currently available: PCE, COD, DRE, E" }

Definition at line 183 of file AvalancheSchedulerSvc.h.

183 { this, "Optimizer", "",
184 "The following modes are currently available: PCE, COD, DRE, E" };

◆ m_precSvc

SmartIF<IPrecedenceSvc> AvalancheSchedulerSvc::m_precSvc
private

A shortcut to the Precedence Service.

Definition at line 262 of file AvalancheSchedulerSvc.h.

◆ m_retryQueue

std::queue<TaskSpec> AvalancheSchedulerSvc::m_retryQueue
private

Definition at line 355 of file AvalancheSchedulerSvc.h.

◆ m_scheduledAsynchronousQueue

tbb::concurrent_priority_queue<TaskSpec, AlgQueueSort> AvalancheSchedulerSvc::m_scheduledAsynchronousQueue
private

Definition at line 354 of file AvalancheSchedulerSvc.h.

◆ m_scheduledQueue

tbb::concurrent_priority_queue<TaskSpec, AlgQueueSort> AvalancheSchedulerSvc::m_scheduledQueue
private

Queues for scheduled algorithms.

Definition at line 353 of file AvalancheSchedulerSvc.h.

◆ m_showControlFlow

Gaudi::Property<bool> AvalancheSchedulerSvc::m_showControlFlow
private
Initial value:
{ this, "ShowControlFlow", false,
"Show the configuration of all Algorithms and Sequences" }

Definition at line 216 of file AvalancheSchedulerSvc.h.

216 { this, "ShowControlFlow", false,
217 "Show the configuration of all Algorithms and Sequences" };

◆ m_showDataDeps

Gaudi::Property<bool> AvalancheSchedulerSvc::m_showDataDeps
private
Initial value:
{ this, "ShowDataDependencies", true,
"Show the INPUT and OUTPUT data dependencies of Algorithms" }

Definition at line 210 of file AvalancheSchedulerSvc.h.

210 { this, "ShowDataDependencies", true,
211 "Show the INPUT and OUTPUT data dependencies of Algorithms" };

◆ m_showDataFlow

Gaudi::Property<bool> AvalancheSchedulerSvc::m_showDataFlow
private
Initial value:
{ this, "ShowDataFlow", false,
"Show the configuration of DataFlow between Algorithms" }

Definition at line 213 of file AvalancheSchedulerSvc.h.

213 { this, "ShowDataFlow", false,
214 "Show the configuration of DataFlow between Algorithms" };

◆ m_simulateExecution

Gaudi::Property<bool> AvalancheSchedulerSvc::m_simulateExecution
private
Initial value:
{
this, "SimulateExecution", false,
"Flag to perform single-pass simulation of execution flow before the actual execution" }

Definition at line 180 of file AvalancheSchedulerSvc.h.

180 {
181 this, "SimulateExecution", false,
182 "Flag to perform single-pass simulation of execution flow before the actual execution" };

◆ m_snapshotCallback

std::function<void( OccupancySnapshot )> AvalancheSchedulerSvc::m_snapshotCallback
private

Definition at line 168 of file AvalancheSchedulerSvc.h.

◆ m_snapshotInterval

std::chrono::duration<int64_t, std::milli> AvalancheSchedulerSvc::m_snapshotInterval = std::chrono::duration<int64_t, std::milli>::min()
private

Definition at line 166 of file AvalancheSchedulerSvc.h.

◆ m_thread

std::thread AvalancheSchedulerSvc::m_thread
private

The thread in which the activate function runs.

Definition at line 247 of file AvalancheSchedulerSvc.h.

◆ m_threadPoolSize

Gaudi::Property<int> AvalancheSchedulerSvc::m_threadPoolSize
private
Initial value:
{
this, "ThreadPoolSize", -1,
"Size of the global thread pool initialised by TBB; a value of -1 requests to use"
"all available hardware threads; -100 requests to bypass TBB executing "
"all algorithms in the scheduler's thread." }

Definition at line 170 of file AvalancheSchedulerSvc.h.

170 {
171 this, "ThreadPoolSize", -1,
172 "Size of the global thread pool initialised by TBB; a value of -1 requests to use"
173 "all available hardware threads; -100 requests to bypass TBB executing "
174 "all algorithms in the scheduler's thread." };

◆ m_threadPoolSvc

SmartIF<IThreadPoolSvc> AvalancheSchedulerSvc::m_threadPoolSvc
private

Definition at line 363 of file AvalancheSchedulerSvc.h.

◆ m_useDataLoader

Gaudi::Property<std::string> AvalancheSchedulerSvc::m_useDataLoader
private
Initial value:
{ this, "DataLoaderAlg", "",
"Attribute unmet input dependencies to this DataLoader Algorithm" }

Definition at line 205 of file AvalancheSchedulerSvc.h.

205 { this, "DataLoaderAlg", "",
206 "Attribute unmet input dependencies to this DataLoader Algorithm" };

◆ m_verboseSubSlots

Gaudi::Property<bool> AvalancheSchedulerSvc::m_verboseSubSlots { this, "VerboseSubSlots", false, "Dump algorithm states for all sub-slots" }
private

Definition at line 219 of file AvalancheSchedulerSvc.h.

219{ this, "VerboseSubSlots", false, "Dump algorithm states for all sub-slots" };

◆ m_whiteboard

SmartIF<IHiveWhiteBoard> AvalancheSchedulerSvc::m_whiteboard
private

A shortcut to the whiteboard.

Definition at line 265 of file AvalancheSchedulerSvc.h.

◆ m_whiteboardSvcName

Gaudi::Property<std::string> AvalancheSchedulerSvc::m_whiteboardSvcName { this, "WhiteboardSvc", "EventDataSvc", "The whiteboard name" }
private

Definition at line 179 of file AvalancheSchedulerSvc.h.

179{ this, "WhiteboardSvc", "EventDataSvc", "The whiteboard name" };

The documentation for this class was generated from the following files: