IntelProfilerAuditor.cpp
Go to the documentation of this file.
1 // ## Includes.
2 // * Standard libraries.
3 #include <algorithm>
4 #include <vector>
5 #include <stack>
6 #include <string>
7 #include <utility>
8 #include <memory>
9 #include <iomanip>
10 #include <sstream>
11 
12 // * Gaudi libraries.
13 #include "GaudiKernel/Auditor.h"
14 #include "GaudiKernel/IAuditorSvc.h"
15 #include "GaudiKernel/GaudiException.h"
16 #include "GaudiKernel/MsgStream.h"
17 #include "GaudiKernel/IIncidentListener.h"
18 #include "GaudiKernel/IIncidentSvc.h"
19 
20 // * Intel User API
21 #ifdef __GNUC__
22 #pragma GCC diagnostic ignored "-Wunused-function"
23 #endif
24 #include "ittnotify.h"
25 
26 typedef std::map<std::string, __itt_event> TaskTypes;
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 Auditor, virtual public IIncidentListener {
32 public:
33  // ## Public functions.
34  IntelProfilerAuditor(const std::string& name, ISvcLocator* pSvcLocator);
36  // Overridden functions.
37  void handle(const Incident& incident);
38  using Auditor::before; // avoid hiding base-class methods
39  void before(StandardEventType type, INamedInterface* i);
40  using Auditor::after; // avoid hiding base-class methods
41  void after(StandardEventType type, INamedInterface* i, const StatusCode& sc);
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_,
48  const __itt_event event_ = 0, const __itt_event parent_event_ = 0):
49  name(name_),
50  status(status_),
51  event(event_),
52  parent_event(parent_event_){}
53  // Name of the component.
54  std::string name;
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 private:
63  // From what event to start profiling. Default = 1.
65  // After what event we stop profiling. If 0 than we also profile finalization
66  // stage. Default = 0.
68  // Names of excluded algorithms.
69  std::vector<std::string> m_excluded;
70  // Names of included algorithms.
71  std::vector<std::string> m_included;
72  // Algorithm name, for which intel amplifier event type will be created.
73  std::vector<std::string> m_algs_for_tasktypes;
74  // The String delimiter between sequences/algorithms names in
75  // "Task Type" grouping at Amplifier.
76  std::string m_alg_delim;
77  // Enable frames (needed for detecting slow events).
79  // Frames rate. The recommended maximum rate for calling the Frame API is
80  // 1000 frames (events) per second. A higher rate may result in large product
81  // memory consumption and slow finalization.
82  // You need update "slow-frames-threshold" and "fast-frames-threshold"
83  // parameters of amplxe-cl tool to separate slow, medium and fast events.
85 private:
86  // Logger.
88  // Events counter.
89  int m_nEvents;
90  // Domain for event loop.
91  __itt_domain* domain;
92  // True if profiler is started.
94  // Current stack of sequences/algorithms.
95  std::vector<stack_entity> m_stack;
96  // Mapping of task type name to Amplifier event .
98 private:
99  // ## Private functions.
100  void start_profiling_component(const std::string& name);
101  void skip_profiling_component(const std::string& name);
102 
103  void start();
104  void pause();
105  void resume();
106  void stop();
107 
108  bool hasIncludes() const;
109  bool isIncluded(const std::string& name) const;
110  bool isExcluded(const std::string& name) const;
111  bool isRunning() const;
112 
113  int stackLevel() const;
114  std::string stackIndent(bool newLevel = false) const;
115  std::string taskTypeName(const std::string& component_name) const;
116 };
117 // ## Implementation.
118 // Constructor
120  ISvcLocator* pSvcLocator) : Auditor(name, pSvcLocator), m_log(msgSvc(), name)
121  ,m_nEvents(0), m_isStarted(false) {
122  // ## Properties
123  declareProperty("IncludeAlgorithms", m_included,
124  "Names of included algorithms."
125  );
126  declareProperty("ExcludeAlgorithms", m_excluded,
127  "Names of excluded algorithms."
128  );
129  declareProperty("StartFromEventN", m_nStartFromEvent = 1,
130  "After what event we stop profiling. "
131  "If 0 than we also profile finalization stage."
132  );
133  declareProperty("StopAtEventN", m_nStopAtEvent = 0,
134  "After what event we stop profiling. "
135  "If 0 than we also profile finalization stage. Default = 0."
136  );
137  declareProperty("ComponentsForTaskTypes", m_algs_for_tasktypes,
138  "Algorithm name, for which intel amplifier task type will be created."
139  "By default all algorithms have a corresponding task type.");
140  declareProperty("TaskTypeNameDelimeter", m_alg_delim = " ",
141  "The String delimiter between sequences/algorithms names in "
142  "\"Task Type\" grouping at Amplifier. Default=\" \"."
143  );
144  declareProperty("EnableFrames", m_enable_frames = false,
145  "Enable frames (needed for detecting slow events). Default=false."
146  );
147  declareProperty("FramesRate", m_frames_rate = 100,
148  "Frames rate. The recommended maximum rate for calling the Frame API is "
149  "1000 frames (events) per second. A higher rate may result in large product"
150  " memory consumption and slow finalization. "
151  "You need update \"slow-frames-threshold\" and \"fast-frames-threshold\" "
152  "parameters of amplxe-cl tool to separate slow, medium and fast events. "
153  "For use frames you need to switch on \"EnableFrames\". "
154  "Default=100"
155  );
156 }
157 
159  m_isStarted = true;
160  __itt_resume();
161 }
162 
164  if (!m_isStarted) return;
165  std::string typeName = taskTypeName(name);
166  __itt_event taskId = 0;
167  TaskTypes::const_iterator iter = m_tasktypes.find(typeName);
168  if( iter != m_tasktypes.end()) {
169  taskId = iter->second;
170  }
171 
172  if(!taskId && m_algs_for_tasktypes.empty()) {
173  // Create event
174  taskId = __itt_event_create(typeName.c_str(), typeName.size());
175  m_tasktypes.insert(TaskTypes::value_type(typeName, taskId));
176  }
177 
178  stack_entity state = stack_entity(name, true, taskId);
179  stack_entity* parent = !m_stack.empty()?&m_stack.back():NULL;
180 
181  if (parent != NULL) {
182  if (parent->event) {
183  state.parent_event = parent->event;
184  } else {
185  state.parent_event = parent->parent_event;
186  }
187  }
188 
189  if (taskId && state.parent_event) {
190  m_log << MSG::DEBUG << stackIndent() << "Pause event " <<
191  state.parent_event << endmsg;
192  __itt_event_end(state.parent_event);
193  }
194  m_stack.push_back(state);
195 
196  m_log << MSG::DEBUG << stackIndent() << "Start profiling component "
197  << typeName << endmsg;
198 
199  if (taskId) {
200  // Start event
201  m_log << MSG::DEBUG << stackIndent() << "Start event type "
202  << state.event << " for " << typeName << endmsg;
203  __itt_event_start(state.event);
204  }
205 
206  __itt_resume();
207 }
208 
210  if (!m_isStarted) return;
211  m_log << MSG::DEBUG << stackIndent() << "Resume" << endmsg;
212  __itt_resume();
213 }
214 
216  if (!m_isStarted) return;
217  m_log << MSG::DEBUG << stackIndent() << "Pause" << endmsg;
218  __itt_pause();
219 }
220 
222  if (!m_isStarted) return;
223  m_stack.push_back(stack_entity(name, false));
224  m_log << MSG::DEBUG << stackIndent() << "Skip component "
225  << name << endmsg;
226 }
227 
229  if (!m_isStarted) return;
230  m_isStarted = false;
231  __itt_pause();
232 }
233 
235  return !m_included.empty();
236 }
237 
238 bool IntelProfilerAuditor::isIncluded(const std::string& name) const {
239  return std::find(m_included.begin(), m_included.end(), name) !=
240  m_included.end();
241 }
242 
243 bool IntelProfilerAuditor::isExcluded(const std::string& name) const {
244  return std::find(m_excluded.begin(), m_excluded.end(), name) !=
245  m_excluded.end();
246 }
247 
249  return !m_stack.empty() && m_stack.back().status;
250 }
251 
253  return m_stack.size();
254 }
255 
256 std::string IntelProfilerAuditor::stackIndent(bool newLevel) const{
257  std::stringstream indent(std::stringstream::out);
258  indent << std::setw(stackLevel()*2+(newLevel?2:0)) << " ";
259  return indent.str();
260 }
261 
262 std::string
263 IntelProfilerAuditor::taskTypeName(const std::string& component_name) const {
264  std::string result;
265  std::string delim = "";
266  for (const auto& value : m_stack) {
267  result += delim+value.name;
268  delim = m_alg_delim;
269  }
270  return result+m_alg_delim+component_name;
271 }
272 
275  m_log << MSG::INFO << "Initialised" << endmsg;
276 
277  IIncidentSvc * inSvc = NULL;
278  const StatusCode sc = serviceLocator()->service("IncidentSvc", inSvc);
279  if (sc.isFailure())
280  return sc;
281  // Useful to start profiling only after some event, we don't need profile
282  // initialization stage. For that we need to count events with BeginEvent
283  // listener.
284  inSvc->addListener(this, IncidentType::BeginEvent);
285  // If the end event number don't setup we finish profiling at the end
286  // of loop. We don't need profiling finalization stage.
287  inSvc->addListener(this, IncidentType::EndProcessing);
288 
289  std::string str_excluded, str_included, str_eventtypes;
290  for (const auto& name : m_excluded) {
291  str_excluded += " " + name;
292  }
293  for (const auto& name : m_included) {
294  str_included += " " + name;
295  }
296  for (const auto& name : m_algs_for_tasktypes) {
297  str_eventtypes += " " + name;
298  }
299 
300  if (!m_included.empty()) {
301  m_log << MSG::INFO << "Included algorithms (" << m_included.size()
302  << "): " << str_included << endmsg;
303  }
304 
305  if (!m_excluded.empty()){
306  m_log << MSG::INFO << "Excluded algorithms (" << m_excluded.size()
307  << "): " << str_excluded << endmsg;
308  }
309 
310  if (!m_algs_for_tasktypes.empty()){
311  m_log << MSG::INFO << "Event types (" << m_algs_for_tasktypes.size()
312  << "): " << str_eventtypes << endmsg;
313  }
314 
315  // Create a profiler domain for detection of slow events.
316  domain = __itt_domain_create("Event loop");
317  domain->flags = m_enable_frames;
318 
319  return StatusCode::SUCCESS;
320 }
321 
322 
323 
324 void IntelProfilerAuditor::handle(const Incident& incident) {
325  if (IncidentType::BeginEvent != incident.type()) return;
326  // Increment the event counter
327  ++m_nEvents;
328 
329  if (m_nStartFromEvent == m_nEvents) {
330  m_log << MSG::INFO << "Start profiling (event #" << m_nEvents << ")"
331  << endmsg;
332  start();
333  }
334 
335  if (m_nStopAtEvent == m_nEvents) {
336  m_log << MSG::INFO << "Stop profiling (event #" << m_nEvents << ")"
337  << endmsg;
338  stop();
339  }
340 }
341 
343  // Skip unnecessary event types.
344  if (!((type == IAuditor::Execute) && m_isStarted)) return;
345 
346  // Name of the current component.
347  const std::string& name = i->name();
348  //m_log << MSG::DEBUG << "Before: " << name << " " << type << endmsg;
349 
350  if (isRunning()) {
351  if (isExcluded(name)) {
352  // If profiling is running and component is explicitly excluded
353  // then skip component.
355  }else{
356  // If profiling is running and component is'not explicitly excluded
357  // then start profiling for component (add to stack).
359  }
360  }else {
361  if (hasIncludes()) {
362  // If the profiling is not running and "includes" is explicitly defined ...
363  if (isIncluded(name)) {
364  // and the current component is in the include's list then start the
365  // component profiling.
367  } else{
368  // and the current component is not in the includes list then skip
369  // a profiling of the component.
371  }
372  }else {
373  // If "Includes" property isn't present and the component is ...
374  if (isExcluded(name)) {
375  // in the excludes list then skip a profiling
377  }else{
378  // not in the exclude list then start a profiling
380  }
381  }
382  }
383  if (m_nEvents % m_frames_rate == 0) {
384  __itt_frame_begin_v3(domain, NULL);
385  }
386 
387 }
388 
389 void IntelProfilerAuditor::after(StandardEventType type,
390  INamedInterface* i, const StatusCode&/* sc*/) {
391  // Skip unnecessary event types
392  if (!((type == IAuditor::Execute) && m_isStarted)) return;
393 
394  if ((m_nEvents+1) % m_frames_rate == 0) {
395  __itt_frame_end_v3(domain, NULL);
396  }
397 
398  // Name of the current component
399  const std::string& name = i->name();
400  stack_entity state = m_stack.back();
401  // Remove component from stack.
402  m_stack.pop_back();
403 
404 
405  if (state.event != 0) {
406  m_log << MSG::DEBUG << stackIndent(true) << "End event for "
407  << name << endmsg;
408  __itt_event_end(state.event);
409 
410  if (state.parent_event != 0) {
411  m_log << MSG::DEBUG << stackIndent() << "Resume event for "
412  << state.parent_event << endmsg;
413  __itt_event_start(state.parent_event);
414  }
415  }
416 
417  if (m_stack.empty()) {
418  // Pause if there are no parent components (top algorithm).
419  pause();
420  } else if (state.status) {
421  // If the profiling is running and we have parent component that is
422  // paused then pause the profiling.
423  if (!m_stack.back().status) {
424  pause();
425  }
426  }else {
427  // If the profiling was stopped, but the parent component should be profiled
428  // then resume profiling.
429  if (m_stack.back().status) {
430  resume();
431  }
432  }
433 }
434 
435 // Register the auditor
void start_profiling_component(const std::string &name)
std::map< std::string, __itt_event > TaskTypes
Definition of the MsgStream class used to transmit messages.
Definition: MsgStream.h:24
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:25
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:244
const std::string & type() const
Access to the incident type.
Definition: Incident.h:34
std::vector< std::string > m_algs_for_tasktypes
void skip_profiling_component(const std::string &name)
bool isExcluded(const std::string &name) const
bool isFailure() const
Test for a status code of FAILURE.
Definition: StatusCode.h:86
void after(StandardEventType type, INamedInterface *i, const StatusCode &sc)
virtual const std::string & name() const =0
Retrieve the name of the instance.
StatusCode service(const Gaudi::Utils::TypeNameString &name, T *&svc, bool createIf=true)
Templated method to access a service by name.
Definition: ISvcLocator.h:78
std::vector< std::string > m_excluded
Property * declareProperty(const std::string &name, T &property, const std::string &doc="none") const
Declare the named property.
Definition: Auditor.h:241
The interface implemented by any class wanting to listen to Incidents.
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:26
#define DECLARE_COMPONENT(type)
Definition: PluginService.h:36
void after(StandardEventType, INamedInterface *, const StatusCode &) override
Definition: Auditor.cpp:116
IntelProfilerAuditor(const std::string &name, ISvcLocator *pSvcLocator)
SmartIF< ISvcLocator > & serviceLocator() const
The standard service locator.
Definition: Auditor.cpp:228
std::vector< stack_entity > m_stack
std::string stackIndent(bool newLevel=false) const
IInterface compliant class extending IInterface with the name() method.
std::string taskTypeName(const std::string &component_name) const
Base class for all Incidents (computing events).
Definition: Incident.h:16
virtual void addListener(IIncidentListener *lis, const std::string &type="", long priority=0, bool rethrow=false, bool singleShot=false)=0
Add listener.
void setLevel(int level)
Update outputlevel.
Definition: MsgStream.h:106
const std::string & name() const override
Definition: Auditor.cpp:212
void before(StandardEventType type, INamedInterface *i)
int outputLevel() const
Retrieve the output level of current auditor.
Definition: Auditor.h:105
bool isIncluded(const std::string &name) const
std::string typeName(const std::type_info &typ)
Definition: Dictionary.cpp:21
list i
Definition: ana.py:128
void before(StandardEventType, INamedInterface *) override
The following methods are meant to be implemented by the child class...
Definition: Auditor.cpp:96
The interface implemented by the IncidentSvc service.
Definition: IIncidentSvc.h:21
string type
Definition: gaudirun.py:151
std::vector< std::string > m_included
Base class from which all concrete auditor classes should be derived.
Definition: Auditor.h:34
void handle(const Incident &incident)
Inform that a new incident has occurred.