The Gaudi Framework  v30r3 (a5ef0a68)
IntelProfilerAuditor.cpp
Go to the documentation of this file.
1 // ## Includes.
2 // * Standard libraries.
3 #include <algorithm>
4 #include <iomanip>
5 #include <memory>
6 #include <sstream>
7 #include <stack>
8 #include <string>
9 #include <utility>
10 #include <vector>
11 
12 // * Gaudi libraries.
13 #include "GaudiKernel/Auditor.h"
18 #include "GaudiKernel/MsgStream.h"
19 
20 // * Intel User API
21 #ifdef __GNUC__
22 #pragma GCC diagnostic ignored "-Wunused-function"
23 #endif
24 #include "ittnotify.h"
25 
27 
28 // Gaudi profiling auditor. The auditor use Intel API for control profiling
29 // flow. We need to run profiling throw Intel Amplifier amplxe-cl command
30 // line tool.
31 class IntelProfilerAuditor : public extends<Auditor, IIncidentListener>
32 {
33 public:
34  // ## Public functions.
35  using extends::extends;
36  StatusCode initialize() override;
37  // Overridden functions.
38  void handle( const Incident& incident ) override;
39  using Auditor::before; // avoid hiding base-class methods
40  void before( StandardEventType type, INamedInterface* i ) override;
41  using Auditor::after; // avoid hiding base-class methods
42  void after( StandardEventType type, INamedInterface* i, const StatusCode& sc ) override;
43  // ## Private attributes.
44 private:
45  // Stack for store current component(algorithm) chain with useful
46  // information for the auditor.
47  struct stack_entity {
48  stack_entity( const std::string& name_, bool status_, const __itt_event event_ = 0,
49  const __itt_event parent_event_ = 0 )
50  : name( name_ ), status( status_ ), event( event_ ), parent_event( parent_event_ )
51  {
52  }
53  // Name of the component.
55  // Running status: on/off.
56  bool status;
57  // Task type holder.
58  __itt_event event;
59  // Parent task type.
60  __itt_event parent_event;
61  };
62 
63 private:
64  Gaudi::Property<std::vector<std::string>> m_included{this, "IncludeAlgorithms", {}, "Names of included algorithms."};
65  Gaudi::Property<std::vector<std::string>> m_excluded{this, "ExcludeAlgorithms", {}, "Names of excluded algorithms."};
67  this, "StartFromEventN", 1, "After what event we stop profiling. If 0 than we also profile finalization stage."};
69  this, "StopAtEventN", 0,
70  "After what event we stop profiling. If 0 than we also profile finalization stage. Default = 0."};
72  this,
73  "ComponentsForTaskTypes",
74  {},
75  "Algorithm name, for which intel amplifier task type will be created."
76  "By default all algorithms have a corresponding task type."};
77  Gaudi::Property<std::string> m_alg_delim{this, "TaskTypeNameDelimeter", " ",
78  "The String delimiter between sequences/algorithms names in "
79  "\"Task Type\" grouping at Amplifier. Default=\" \"."};
80  Gaudi::Property<bool> m_enable_frames{this, "EnableFrames", false,
81  "Enable frames (needed for detecting slow events). Default=false."};
82  Gaudi::Property<int> m_frames_rate{this, "FramesRate", 100,
83  "Frames rate. The recommended maximum rate for calling the Frame API is "
84  "1000 frames (events) per second. A higher rate may result in large product"
85  " memory consumption and slow finalization. "
86  "You need update \"slow-frames-threshold\" and \"fast-frames-threshold\" "
87  "parameters of amplxe-cl tool to separate slow, medium and fast events. "
88  "For use frames you need to switch on \"EnableFrames\". "
89  "Default=100"};
90 
91 private:
92  // Events counter.
93  int m_nEvents = 0;
94  // Domain for event loop.
95  __itt_domain* domain = nullptr;
96  // True if profiler is started.
97  bool m_isStarted = false;
98  // Current stack of sequences/algorithms.
100  // Mapping of task type name to Amplifier event .
102 
103 private:
104  // ## Private functions.
107 
108  void start();
109  void pause();
110  void resume();
111  void stop();
112 
113  bool hasIncludes() const;
114  bool isIncluded( const std::string& name ) const;
115  bool isExcluded( const std::string& name ) const;
116  bool isRunning() const;
117 
118  int stackLevel() const;
119  std::string stackIndent( bool newLevel = false ) const;
120  std::string taskTypeName( const std::string& component_name ) const;
121 };
122 // ## Implementation.
124 {
125  m_isStarted = true;
126  __itt_resume();
127 }
128 
130 {
131  if ( !m_isStarted ) return;
133  __itt_event taskId = 0;
134  TaskTypes::const_iterator iter = m_tasktypes.find( typeName );
135  if ( iter != m_tasktypes.end() ) {
136  taskId = iter->second;
137  }
138 
139  if ( !taskId && m_algs_for_tasktypes.empty() ) {
140  // Create event
141  taskId = __itt_event_create( typeName.c_str(), typeName.size() );
142  m_tasktypes.insert( TaskTypes::value_type( typeName, taskId ) );
143  }
144 
145  stack_entity state = stack_entity( name, true, taskId );
146  stack_entity* parent = !m_stack.empty() ? &m_stack.back() : NULL;
147 
148  if ( parent != NULL ) {
149  if ( parent->event ) {
150  state.parent_event = parent->event;
151  } else {
152  state.parent_event = parent->parent_event;
153  }
154  }
155 
156  if ( taskId && state.parent_event ) {
157  debug() << stackIndent() << "Pause event " << state.parent_event << endmsg;
158  __itt_event_end( state.parent_event );
159  }
160  m_stack.push_back( state );
161 
162  debug() << stackIndent() << "Start profiling component " << typeName << endmsg;
163 
164  if ( taskId ) {
165  // Start event
166  debug() << stackIndent() << "Start event type " << state.event << " for " << typeName << endmsg;
167  __itt_event_start( state.event );
168  }
169 
170  __itt_resume();
171 }
172 
174 {
175  if ( !m_isStarted ) return;
176  debug() << stackIndent() << "Resume" << endmsg;
177  __itt_resume();
178 }
179 
181 {
182  if ( !m_isStarted ) return;
183  debug() << stackIndent() << "Pause" << endmsg;
184  __itt_pause();
185 }
186 
188 {
189  if ( !m_isStarted ) return;
190  m_stack.push_back( stack_entity( name, false ) );
191  debug() << stackIndent() << "Skip component " << name << endmsg;
192 }
193 
195 {
196  if ( !m_isStarted ) return;
197  m_isStarted = false;
198  __itt_pause();
199 }
200 
201 bool IntelProfilerAuditor::hasIncludes() const { return !m_included.empty(); }
202 
204 {
205  return std::find( m_included.begin(), m_included.end(), name ) != m_included.end();
206 }
207 
209 {
210  return std::find( m_excluded.begin(), m_excluded.end(), name ) != m_excluded.end();
211 }
212 
213 bool IntelProfilerAuditor::isRunning() const { return !m_stack.empty() && m_stack.back().status; }
214 
215 int IntelProfilerAuditor::stackLevel() const { return m_stack.size(); }
216 
218 {
219  std::stringstream indent( std::stringstream::out );
220  indent << std::setw( stackLevel() * 2 + ( newLevel ? 2 : 0 ) ) << " ";
221  return indent.str();
222 }
223 
225 {
226  std::string result;
227  std::string delim = "";
228  for ( const auto& value : m_stack ) {
229  result += delim + value.name;
230  delim = m_alg_delim;
231  }
232  return result + m_alg_delim + component_name;
233 }
234 
236 {
237  info() << "Initialised" << endmsg;
238 
239  IIncidentSvc* inSvc = NULL;
240  const StatusCode sc = serviceLocator()->service( "IncidentSvc", inSvc );
241  if ( sc.isFailure() ) return sc;
242  // Useful to start profiling only after some event, we don't need profile
243  // initialization stage. For that we need to count events with BeginEvent
244  // listener.
245  inSvc->addListener( this, IncidentType::BeginEvent );
246  // If the end event number don't setup we finish profiling at the end
247  // of loop. We don't need profiling finalization stage.
248  inSvc->addListener( this, IncidentType::EndProcessing );
249 
250  std::string str_excluded, str_included, str_eventtypes;
251  for ( const auto& name : m_excluded ) {
252  str_excluded += " " + name;
253  }
254  for ( const auto& name : m_included ) {
255  str_included += " " + name;
256  }
257  for ( const auto& name : m_algs_for_tasktypes ) {
258  str_eventtypes += " " + name;
259  }
260 
261  if ( !m_included.empty() ) {
262  info() << "Included algorithms (" << m_included.size() << "): " << str_included << endmsg;
263  }
264 
265  if ( !m_excluded.empty() ) {
266  info() << "Excluded algorithms (" << m_excluded.size() << "): " << str_excluded << endmsg;
267  }
268 
269  if ( !m_algs_for_tasktypes.empty() ) {
270  info() << "Event types (" << m_algs_for_tasktypes.size() << "): " << str_eventtypes << endmsg;
271  }
272 
273  // Create a profiler domain for detection of slow events.
274  domain = __itt_domain_create( "Event loop" );
275  domain->flags = m_enable_frames;
276 
277  return StatusCode::SUCCESS;
278 }
279 
280 void IntelProfilerAuditor::handle( const Incident& incident )
281 {
282  if ( IncidentType::BeginEvent != incident.type() ) return;
283  // Increment the event counter
284  ++m_nEvents;
285 
286  if ( m_nStartFromEvent == m_nEvents ) {
287  info() << "Start profiling (event #" << m_nEvents << ")" << endmsg;
288  start();
289  }
290 
291  if ( m_nStopAtEvent == m_nEvents ) {
292  info() << "Stop profiling (event #" << m_nEvents << ")" << endmsg;
293  stop();
294  }
295 }
296 
297 void IntelProfilerAuditor::before( StandardEventType type, INamedInterface* i )
298 {
299  // Skip unnecessary event types.
300  if ( !( ( type == IAuditor::Execute ) && m_isStarted ) ) return;
301 
302  // Name of the current component.
303  const std::string& name = i->name();
304  // debug() << "Before: " << name << " " << type << endmsg;
305 
306  if ( isRunning() ) {
307  if ( isExcluded( name ) ) {
308  // If profiling is running and component is explicitly excluded
309  // then skip component.
310  skip_profiling_component( name );
311  } else {
312  // If profiling is running and component is'not explicitly excluded
313  // then start profiling for component (add to stack).
315  }
316  } else {
317  if ( hasIncludes() ) {
318  // If the profiling is not running and "includes" is explicitly defined ...
319  if ( isIncluded( name ) ) {
320  // and the current component is in the include's list then start the
321  // component profiling.
323  } else {
324  // and the current component is not in the includes list then skip
325  // a profiling of the component.
326  skip_profiling_component( name );
327  }
328  } else {
329  // If "Includes" property isn't present and the component is ...
330  if ( isExcluded( name ) ) {
331  // in the excludes list then skip a profiling
332  skip_profiling_component( name );
333  } else {
334  // not in the exclude list then start a profiling
336  }
337  }
338  }
339  if ( m_nEvents % m_frames_rate == 0 ) {
340  __itt_frame_begin_v3( domain, NULL );
341  }
342 }
343 
344 void IntelProfilerAuditor::after( StandardEventType type, INamedInterface* i, const StatusCode& /* sc*/ )
345 {
346  // Skip unnecessary event types
347  if ( !( ( type == IAuditor::Execute ) && m_isStarted ) ) return;
348 
349  if ( ( m_nEvents + 1 ) % m_frames_rate == 0 ) {
350  __itt_frame_end_v3( domain, NULL );
351  }
352 
353  // Name of the current component
354  const std::string& name = i->name();
355  stack_entity state = m_stack.back();
356  // Remove component from stack.
357  m_stack.pop_back();
358 
359  if ( state.event != 0 ) {
360  debug() << stackIndent( true ) << "End event for " << name << endmsg;
361  __itt_event_end( state.event );
362 
363  if ( state.parent_event != 0 ) {
364  debug() << stackIndent() << "Resume event for " << state.parent_event << endmsg;
365  __itt_event_start( state.parent_event );
366  }
367  }
368 
369  if ( m_stack.empty() ) {
370  // Pause if there are no parent components (top algorithm).
371  pause();
372  } else if ( state.status ) {
373  // If the profiling is running and we have parent component that is
374  // paused then pause the profiling.
375  if ( !m_stack.back().status ) {
376  pause();
377  }
378  } else {
379  // If the profiling was stopped, but the parent component should be profiled
380  // then resume profiling.
381  if ( m_stack.back().status ) {
382  resume();
383  }
384  }
385 }
386 
387 // Register the auditor
void start_profiling_component(const std::string &name)
std::map< std::string, __itt_event > TaskTypes
Gaudi::Property< int > m_nStartFromEvent
Gaudi::Property< int > m_nStopAtEvent
const std::string & type() const
Access to the incident type.
Definition: Incident.h:41
Implementation of property with value of concrete type.
Definition: Property.h:381
MsgStream & info() const
shortcut for the method msgStream(MSG::INFO)
void after(StandardEventType type, INamedInterface *i, const StatusCode &sc) override
Gaudi::Property< std::vector< std::string > > m_algs_for_tasktypes
void skip_profiling_component(const std::string &name)
Gaudi::Property< std::vector< std::string > > m_included
bool isExcluded(const std::string &name) const
T end(T...args)
StatusCode initialize() override
Gaudi::Property< int > m_frames_rate
bool isFailure() const
Definition: StatusCode.h:139
T setw(T...args)
STL class.
#define DECLARE_COMPONENT(type)
StatusCode service(const Gaudi::Utils::TypeNameString &name, T *&svc, bool createIf=true)
Templated method to access a service by name.
Definition: ISvcLocator.h:79
stack_entity(const std::string &name_, bool status_, const __itt_event event_=0, const __itt_event parent_event_=0)
This class is used for returning status codes from appropriate routines.
Definition: StatusCode.h:51
SmartIF< ISvcLocator > & serviceLocator() const override
The standard service locator.
Definition: Auditor.cpp:206
void after(StandardEventType, INamedInterface *, const StatusCode &) override
Definition: Auditor.cpp:110
Gaudi::Property< std::vector< std::string > > m_excluded
T str(T...args)
constexpr static const auto SUCCESS
Definition: StatusCode.h:87
Gaudi::Property< bool > m_enable_frames
std::vector< stack_entity > m_stack
std::string stackIndent(bool newLevel=false) const
T insert(T...args)
IInterface compliant class extending IInterface with the name() method.
T find(T...args)
std::string taskTypeName(const std::string &component_name) const
T size(T...args)
Gaudi::Property< std::string > m_alg_delim
STL class.
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
Base class used to extend a class implementing other interfaces.
Definition: extends.h:10
void handle(const Incident &incident) override
T c_str(T...args)
Base class for all Incidents (computing events).
Definition: Incident.h:17
virtual void addListener(IIncidentListener *lis, const std::string &type="", long priority=0, bool rethrow=false, bool singleShot=false)=0
Add listener.
bool isIncluded(const std::string &name) const
std::string typeName(const std::type_info &typ)
Definition: Dictionary.cpp:23
void before(StandardEventType, INamedInterface *) override
The following methods are meant to be implemented by the child class...
Definition: Auditor.cpp:73
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:209
virtual const std::string & name() const =0
Retrieve the name of the instance.
The interface implemented by the IncidentSvc service.
Definition: IIncidentSvc.h:23
void before(StandardEventType type, INamedInterface *i) override