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