Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v31r0 (aeb156f0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 public:
33  // ## Public functions.
34  using extends::extends;
35  StatusCode initialize() override;
36  // Overridden functions.
37  void handle( const Incident& incident ) override;
38  using Auditor::before; // avoid hiding base-class methods
39  void before( StandardEventType type, INamedInterface* i ) override;
40  using Auditor::after; // avoid hiding base-class methods
41  void after( StandardEventType type, INamedInterface* i, const StatusCode& sc ) override;
42  // ## Private attributes.
43 private:
44  // Stack for store current component(algorithm) chain with useful
45  // information for the auditor.
46  struct stack_entity {
47  stack_entity( const std::string& name_, bool status_, const __itt_event event_ = 0,
48  const __itt_event parent_event_ = 0 )
49  : name( name_ ), status( status_ ), event( event_ ), parent_event( parent_event_ ) {}
50  // Name of the component.
52  // Running status: on/off.
53  bool status;
54  // Task type holder.
55  __itt_event event;
56  // Parent task type.
57  __itt_event parent_event;
58  };
59 
60 private:
61  Gaudi::Property<std::vector<std::string>> m_included{this, "IncludeAlgorithms", {}, "Names of included algorithms."};
62  Gaudi::Property<std::vector<std::string>> m_excluded{this, "ExcludeAlgorithms", {}, "Names of excluded algorithms."};
64  this, "StartFromEventN", 1, "After what event we stop profiling. If 0 than we also profile finalization stage."};
66  this, "StopAtEventN", 0,
67  "After what event we stop profiling. If 0 than we also profile finalization stage. Default = 0."};
69  this,
70  "ComponentsForTaskTypes",
71  {},
72  "Algorithm name, for which intel amplifier task type will be created."
73  "By default all algorithms have a corresponding task type."};
74  Gaudi::Property<std::string> m_alg_delim{this, "TaskTypeNameDelimeter", " ",
75  "The String delimiter between sequences/algorithms names in "
76  "\"Task Type\" grouping at Amplifier. Default=\" \"."};
77  Gaudi::Property<bool> m_enable_frames{this, "EnableFrames", false,
78  "Enable frames (needed for detecting slow events). Default=false."};
79  Gaudi::Property<int> m_frames_rate{this, "FramesRate", 100,
80  "Frames rate. The recommended maximum rate for calling the Frame API is "
81  "1000 frames (events) per second. A higher rate may result in large product"
82  " memory consumption and slow finalization. "
83  "You need update \"slow-frames-threshold\" and \"fast-frames-threshold\" "
84  "parameters of amplxe-cl tool to separate slow, medium and fast events. "
85  "For use frames you need to switch on \"EnableFrames\". "
86  "Default=100"};
87 
88 private:
89  // Events counter.
90  int m_nEvents = 0;
91  // Domain for event loop.
92  __itt_domain* domain = nullptr;
93  // True if profiler is started.
94  bool m_isStarted = false;
95  // Current stack of sequences/algorithms.
97  // Mapping of task type name to Amplifier event .
99 
100 private:
101  // ## Private functions.
104 
105  void start();
106  void pause();
107  void resume();
108  void stop();
109 
110  bool hasIncludes() const;
111  bool isIncluded( const std::string& name ) const;
112  bool isExcluded( const std::string& name ) const;
113  bool isRunning() const;
114 
115  int stackLevel() const;
116  std::string stackIndent( bool newLevel = false ) const;
117  std::string taskTypeName( const std::string& component_name ) const;
118 };
119 // ## Implementation.
121  m_isStarted = true;
122  __itt_resume();
123 }
124 
126  if ( !m_isStarted ) return;
128  __itt_event taskId = 0;
129  TaskTypes::const_iterator iter = m_tasktypes.find( typeName );
130  if ( iter != m_tasktypes.end() ) { taskId = iter->second; }
131 
132  if ( !taskId && m_algs_for_tasktypes.empty() ) {
133  // Create event
134  taskId = __itt_event_create( typeName.c_str(), typeName.size() );
135  m_tasktypes.insert( TaskTypes::value_type( typeName, taskId ) );
136  }
137 
138  stack_entity state = stack_entity( name, true, taskId );
139  stack_entity* parent = !m_stack.empty() ? &m_stack.back() : NULL;
140 
141  if ( parent != NULL ) {
142  if ( parent->event ) {
143  state.parent_event = parent->event;
144  } else {
145  state.parent_event = parent->parent_event;
146  }
147  }
148 
149  if ( taskId && state.parent_event ) {
150  debug() << stackIndent() << "Pause event " << state.parent_event << endmsg;
151  __itt_event_end( state.parent_event );
152  }
153  m_stack.push_back( state );
154 
155  debug() << stackIndent() << "Start profiling component " << typeName << endmsg;
156 
157  if ( taskId ) {
158  // Start event
159  debug() << stackIndent() << "Start event type " << state.event << " for " << typeName << endmsg;
160  __itt_event_start( state.event );
161  }
162 
163  __itt_resume();
164 }
165 
167  if ( !m_isStarted ) return;
168  debug() << stackIndent() << "Resume" << endmsg;
169  __itt_resume();
170 }
171 
173  if ( !m_isStarted ) return;
174  debug() << stackIndent() << "Pause" << endmsg;
175  __itt_pause();
176 }
177 
179  if ( !m_isStarted ) return;
180  m_stack.push_back( stack_entity( name, false ) );
181  debug() << stackIndent() << "Skip component " << name << endmsg;
182 }
183 
185  if ( !m_isStarted ) return;
186  m_isStarted = false;
187  __itt_pause();
188 }
189 
190 bool IntelProfilerAuditor::hasIncludes() const { return !m_included.empty(); }
191 
193  return std::find( m_included.begin(), m_included.end(), name ) != m_included.end();
194 }
195 
197  return std::find( m_excluded.begin(), m_excluded.end(), name ) != m_excluded.end();
198 }
199 
200 bool IntelProfilerAuditor::isRunning() const { return !m_stack.empty() && m_stack.back().status; }
201 
202 int IntelProfilerAuditor::stackLevel() const { return m_stack.size(); }
203 
205  std::stringstream indent( std::stringstream::out );
206  indent << std::setw( stackLevel() * 2 + ( newLevel ? 2 : 0 ) ) << " ";
207  return indent.str();
208 }
209 
211  std::string result;
212  std::string delim = "";
213  for ( const auto& value : m_stack ) {
214  result += delim + value.name;
215  delim = m_alg_delim;
216  }
217  return result + m_alg_delim + component_name;
218 }
219 
221  info() << "Initialised" << endmsg;
222 
223  IIncidentSvc* inSvc = NULL;
224  const StatusCode sc = serviceLocator()->service( "IncidentSvc", inSvc );
225  if ( sc.isFailure() ) return sc;
226  // Useful to start profiling only after some event, we don't need profile
227  // initialization stage. For that we need to count events with BeginEvent
228  // listener.
229  inSvc->addListener( this, IncidentType::BeginEvent );
230  // If the end event number don't setup we finish profiling at the end
231  // of loop. We don't need profiling finalization stage.
232  inSvc->addListener( this, IncidentType::EndProcessing );
233 
234  std::string str_excluded, str_included, str_eventtypes;
235  for ( const auto& name : m_excluded ) { str_excluded += " " + name; }
236  for ( const auto& name : m_included ) { str_included += " " + name; }
237  for ( const auto& name : m_algs_for_tasktypes ) { str_eventtypes += " " + name; }
238 
239  if ( !m_included.empty() ) {
240  info() << "Included algorithms (" << m_included.size() << "): " << str_included << endmsg;
241  }
242 
243  if ( !m_excluded.empty() ) {
244  info() << "Excluded algorithms (" << m_excluded.size() << "): " << str_excluded << endmsg;
245  }
246 
247  if ( !m_algs_for_tasktypes.empty() ) {
248  info() << "Event types (" << m_algs_for_tasktypes.size() << "): " << str_eventtypes << endmsg;
249  }
250 
251  // Create a profiler domain for detection of slow events.
252  domain = __itt_domain_create( "Event loop" );
253  domain->flags = m_enable_frames;
254 
255  return StatusCode::SUCCESS;
256 }
257 
258 void IntelProfilerAuditor::handle( const Incident& incident ) {
259  if ( IncidentType::BeginEvent != incident.type() ) return;
260  // Increment the event counter
261  ++m_nEvents;
262 
263  if ( m_nStartFromEvent == m_nEvents ) {
264  info() << "Start profiling (event #" << m_nEvents << ")" << endmsg;
265  start();
266  }
267 
268  if ( m_nStopAtEvent == m_nEvents ) {
269  info() << "Stop profiling (event #" << m_nEvents << ")" << endmsg;
270  stop();
271  }
272 }
273 
274 void IntelProfilerAuditor::before( StandardEventType type, INamedInterface* i ) {
275  // Skip unnecessary event types.
276  if ( !( ( type == IAuditor::Execute ) && m_isStarted ) ) return;
277 
278  // Name of the current component.
279  const std::string& name = i->name();
280  // debug() << "Before: " << name << " " << type << endmsg;
281 
282  if ( isRunning() ) {
283  if ( isExcluded( name ) ) {
284  // If profiling is running and component is explicitly excluded
285  // then skip component.
286  skip_profiling_component( name );
287  } else {
288  // If profiling is running and component is'not explicitly excluded
289  // then start profiling for component (add to stack).
291  }
292  } else {
293  if ( hasIncludes() ) {
294  // If the profiling is not running and "includes" is explicitly defined ...
295  if ( isIncluded( name ) ) {
296  // and the current component is in the include's list then start the
297  // component profiling.
299  } else {
300  // and the current component is not in the includes list then skip
301  // a profiling of the component.
302  skip_profiling_component( name );
303  }
304  } else {
305  // If "Includes" property isn't present and the component is ...
306  if ( isExcluded( name ) ) {
307  // in the excludes list then skip a profiling
308  skip_profiling_component( name );
309  } else {
310  // not in the exclude list then start a profiling
312  }
313  }
314  }
315  if ( m_nEvents % m_frames_rate == 0 ) { __itt_frame_begin_v3( domain, NULL ); }
316 }
317 
318 void IntelProfilerAuditor::after( StandardEventType type, INamedInterface* i, const StatusCode& /* sc*/ ) {
319  // Skip unnecessary event types
320  if ( !( ( type == IAuditor::Execute ) && m_isStarted ) ) return;
321 
322  if ( ( m_nEvents + 1 ) % m_frames_rate == 0 ) { __itt_frame_end_v3( domain, NULL ); }
323 
324  // Name of the current component
325  const std::string& name = i->name();
326  stack_entity state = m_stack.back();
327  // Remove component from stack.
328  m_stack.pop_back();
329 
330  if ( state.event != 0 ) {
331  debug() << stackIndent( true ) << "End event for " << name << endmsg;
332  __itt_event_end( state.event );
333 
334  if ( state.parent_event != 0 ) {
335  debug() << stackIndent() << "Resume event for " << state.parent_event << endmsg;
336  __itt_event_start( state.parent_event );
337  }
338  }
339 
340  if ( m_stack.empty() ) {
341  // Pause if there are no parent components (top algorithm).
342  pause();
343  } else if ( state.status ) {
344  // If the profiling is running and we have parent component that is
345  // paused then pause the profiling.
346  if ( !m_stack.back().status ) { pause(); }
347  } else {
348  // If the profiling was stopped, but the parent component should be profiled
349  // then resume profiling.
350  if ( m_stack.back().status ) { resume(); }
351  }
352 }
353 
354 // 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:38
Implementation of property with value of concrete type.
Definition: Property.h:352
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)
constexpr static const auto SUCCESS
Definition: StatusCode.h:85
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:130
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:76
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:50
SmartIF< ISvcLocator > & serviceLocator() const override
The standard service locator.
Definition: Auditor.cpp:197
void after(StandardEventType, INamedInterface *, const StatusCode &) override
Definition: Auditor.cpp:103
Gaudi::Property< std::vector< std::string > > m_excluded
T str(T...args)
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:21
void before(StandardEventType, INamedInterface *) override
The following methods are meant to be implemented by the child class...
Definition: Auditor.cpp:67
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:192
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