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