• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include "ProfilingEvent.hpp"
8 
9 #include <armnn/utility/IgnoreUnused.hpp>
10 #include "armnn/IProfiler.hpp"
11 
12 #include "WallClockTimer.hpp"
13 
14 #include <chrono>
15 #include <iosfwd>
16 #include <ctime>
17 #include <vector>
18 #include <stack>
19 #include <map>
20 
21 namespace armnn
22 {
23 
24 // Simple single-threaded profiler.
25 // Tracks events reported by BeginEvent()/EndEvent() and outputs detailed information and stats when
26 // Profiler::AnalyzeEventsAndWriteResults() is called.
27 class Profiler final : public IProfiler
28 {
29 public:
30     Profiler();
31     ~Profiler();
32     using InstrumentPtr = std::unique_ptr<Instrument>;
33 
34     // Marks the beginning of a user-defined event.
35     // No attempt will be made to copy the name string: it must be known at compile time.
36     Event* BeginEvent(const BackendId& backendId, const std::string& name, std::vector<InstrumentPtr>&& instruments);
37 
38     // Marks the end of a user-defined event.
39     void EndEvent(Event* event);
40 
41     // Enables/disables profiling.
42     void EnableProfiling(bool enableProfiling) override;
43 
44     // Checks if profiling is enabled.
45     bool IsProfilingEnabled() override;
46 
47     // Increments the event tag, allowing grouping of events in a user-defined manner (e.g. per inference).
48     void UpdateEventTag();
49 
50     // Analyzes the tracked events and writes the results to the given output stream.
51     // Please refer to the configuration variables in Profiling.cpp to customize the information written.
52     void AnalyzeEventsAndWriteResults(std::ostream& outStream) const override;
53 
54     // Print stats for events in JSON Format to the given output stream.
55     void Print(std::ostream& outStream) const override;
56 
57     // Gets the color to render an event with, based on which device it denotes.
58     uint32_t GetEventColor(const BackendId& backendId) const;
59 
60 private:
61     using EventPtr = std::unique_ptr<Event>;
62     struct Marker
63     {
64         std::size_t m_Id;
65     };
66 
67     struct ProfilingEventStats
68     {
69         double m_TotalMs;
70         double m_MinMs;
71         double m_MaxMs;
72         uint32_t m_Count;
73     };
74 
75     template<typename EventIterType>
76     void AnalyzeEventSequenceAndWriteResults(EventIterType first, EventIterType last, std::ostream& outStream) const;
77 
78     std::map<std::string, ProfilingEventStats> CalculateProfilingEventStats() const;
79     void PopulateInferences(std::vector<const Event*>& outInferences, int& outBaseLevel) const;
80     void PopulateDescendants(std::map<const Event*, std::vector<const Event*>>& outDescendantsMap) const;
81 
82     std::stack<Event*> m_Parents;
83     std::vector<EventPtr> m_EventSequence;
84     bool m_ProfilingEnabled;
85 
86 private:
87     // Friend functions for unit testing, see ProfilerTests.cpp.
88     friend size_t GetProfilerEventSequenceSize(armnn::Profiler* profiler);
89 };
90 
91 // Singleton profiler manager.
92 // Keeps track of all the running profiler instances.
93 class ProfilerManager
94 {
95 public:
96     // Register the given profiler as a thread local pointer.
97     void RegisterProfiler(Profiler* profiler);
98 
99     // Gets the thread local pointer to the profiler.
100     Profiler* GetProfiler();
101 
102     // Accesses the singleton.
103     static ProfilerManager& GetInstance();
104 
105 private:
106     // The constructor is kept private so that other instances of this class (other that the singleton's)
107     // can't be allocated.
ProfilerManager()108     ProfilerManager() {}
109 };
110 
111 // Helper to easily add event markers to the codebase.
112 class ScopedProfilingEvent
113 {
114 public:
115     using InstrumentPtr = std::unique_ptr<Instrument>;
116 
117     template<typename... Args>
ScopedProfilingEvent(const BackendId & backendId,const std::string & name,Args &&...args)118     ScopedProfilingEvent(const BackendId& backendId, const std::string& name, Args&&... args)
119         : m_Event(nullptr)
120         , m_Profiler(ProfilerManager::GetInstance().GetProfiler())
121     {
122         if (m_Profiler && m_Profiler->IsProfilingEnabled())
123         {
124             std::vector<InstrumentPtr> instruments(0);
125             instruments.reserve(sizeof...(args)); //One allocation
126             ConstructNextInVector(instruments, std::forward<Args>(args)...);
127             m_Event = m_Profiler->BeginEvent(backendId, name, std::move(instruments));
128         }
129     }
130 
~ScopedProfilingEvent()131     ~ScopedProfilingEvent()
132     {
133         if (m_Profiler && m_Event)
134         {
135             m_Profiler->EndEvent(m_Event);
136         }
137     }
138 
139 private:
140 
ConstructNextInVector(std::vector<InstrumentPtr> & instruments)141     void ConstructNextInVector(std::vector<InstrumentPtr>& instruments)
142     {
143         IgnoreUnused(instruments);
144     }
145 
146     template<typename Arg, typename... Args>
ConstructNextInVector(std::vector<InstrumentPtr> & instruments,Arg && arg,Args &&...args)147     void ConstructNextInVector(std::vector<InstrumentPtr>& instruments, Arg&& arg, Args&&... args)
148     {
149         instruments.emplace_back(std::make_unique<Arg>(std::forward<Arg>(arg)));
150         ConstructNextInVector(instruments, std::forward<Args>(args)...);
151     }
152 
153     Event* m_Event;       ///< Event to track
154     Profiler* m_Profiler; ///< Profiler used
155 };
156 
157 } // namespace armnn
158 
159 #define ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS_UNIQUE_LOC_INNER(lineNumber, backendId, /*name,*/ ...) \
160     armnn::ScopedProfilingEvent e_ ## lineNumber(backendId, /*name,*/ __VA_ARGS__);
161 
162 #define ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS_UNIQUE_LOC(lineNumber, backendId, /*name,*/ ...) \
163     ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS_UNIQUE_LOC_INNER(lineNumber, backendId, /*name,*/ __VA_ARGS__)
164 
165 // The event name must be known at compile time i.e. if you are going to use this version of the macro
166 // in code the first argument you supply after the backendId must be the name.
167 // NOTE: need to pass the line number as an argument from here so by the time it gets to the UNIQUE_LOC_INNER
168 //       above it has expanded to a string and will concat (##) correctly with the 'e_' prefix to yield a
169 //       legal and unique variable name (so long as you don't use the macro twice on the same line).
170 //       The concat preprocessing operator (##) very unhelpfully will not expand macros see
171 //       https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html for the gory details.
172 #define ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(backendId, /*name,*/ ...) \
173     ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS_UNIQUE_LOC(__LINE__,backendId, /*name,*/ __VA_ARGS__)
174 
175 #define ARMNN_SCOPED_PROFILING_EVENT(backendId, name) \
176     ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(backendId, name, armnn::WallClockTimer())
177