aboutsummaryrefslogtreecommitdiff
path: root/src/armnn/Profiling.hpp
blob: 88a7adff7c87ab1292930b1b4a9aaeb306c22179 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//
// Copyright © 2017 Arm Ltd. All rights reserved.
// See LICENSE file in the project root for full license information.
//
#pragma once

#if ARMNN_PROFILING_ENABLED

#include "armnn/ArmNN.hpp"

#include <chrono>
#include <iosfwd>
#include <ctime>
#include <vector>
#include <stack>
#include <map>

namespace armnn
{

// Clock class that uses the same timestamp function as the Mali DDK
class monotonic_clock {
public:
    using duration = std::chrono::nanoseconds;
    using time_point = std::chrono::time_point<monotonic_clock, duration>;

    static std::chrono::time_point<monotonic_clock, std::chrono::nanoseconds> now() noexcept
    {
        timespec ts;
#if defined(CLOCK_MONOTONIC_RAW)
        clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
#else
        clock_gettime(CLOCK_MONOTONIC, &ts);
#endif
        return time_point(std::chrono::nanoseconds(ts.tv_sec*1000000000 + ts.tv_nsec));
    }
};

// Simple single-threaded profiler.
// Tracks events reported by BeginEvent()/EndEvent() and outputs detailed information and stats when
// Profiler::AnalyzeEventsAndWriteResults() is called.
class Profiler
{
public:
    // Marks the beginning of a user-defined event.
    // No attempt will be made to copy the name string: It must be known at compile time.
    void BeginEvent(Compute compute, const std::string name);

    // Marks the end of a user-defined event.
    void EndEvent(Compute compute);

    // Increments the event tag, allowing grouping of events in a user-defined manner (e.g. per inference).
    void UpdateEventTag() { ++m_EventTag; m_EventTagUpdated = true; }

    // Analyzes the tracked events and writes the results to the given output stream.
    // Please refer to the configuration variables in Profiling.cpp to customize the information written.
    void AnalyzeEventsAndWriteResults(std::ostream& outStream) const;

    // Accesses the singleton
    static Profiler& Get() { return s_Instance; }

    // Gets a string name for a given Compute device enum
    const char* GetEventComputeDevice(Compute compute) const;

    // Gets the color to render an event with, based on which device it denotes
    std::uint32_t GetEventColor(Compute compute) const;

    typedef monotonic_clock Clock;
    typedef std::chrono::time_point<Clock> TimePoint;

private:

    struct Marker
    {
        std::size_t m_Id;
        const std::string m_EventName;
        TimePoint m_TimeStamp;
        Compute m_ComputeDevice;
        std::uint32_t m_Tag;
    };

    struct ProfilingEvent
    {
        std::string m_Label;
        TimePoint m_StartTime;
        TimePoint m_StopTime;
        Compute m_Device;
        std::uint32_t m_Tag;

        double DurationMs() const
        {
            return std::chrono::duration<double>(m_StopTime - m_StartTime).count()*1000.0;
        }
    };

    struct ProfilingEventStats
    {
        double m_TotalMs;
        double m_MinMs;
        double m_MaxMs;
        std::uint32_t m_Count;
    };

    Profiler();
    ~Profiler();

    // Waits for a compute device to finish working to guarantee correct timings.
    // Currently used exclusively when emitting profiling events denoting GPU work.
    void WaitForDevice(Compute compute) const;

    void AnalyzeEventSequenceAndWriteResults(std::vector<ProfilingEvent>::const_iterator first,
                                             std::vector<ProfilingEvent>::const_iterator last,
                                             std::ostream& outStream) const;

    std::map<std::string, ProfilingEventStats> CalculateProfilingEventStats() const;

    std::stack<Marker> m_ObservedMarkers;
    std::vector<ProfilingEvent> m_EventSequence;
    std::uint32_t m_EventTag;
    std::uint32_t m_NestingLevel;
    bool m_EventTagUpdated;

    static Profiler s_Instance;
};

// Helper to easily add event markers to the codebase
class ScopedProfilingEvent
{
public:
    ScopedProfilingEvent(Compute compute, const std::string name)
        : m_Compute(compute)
    {
        Profiler::Get().BeginEvent(compute, name);
    }

    ~ScopedProfilingEvent()
    {
        Profiler::Get().EndEvent(m_Compute);
    }

private:
    armnn::Compute m_Compute;
};

} // namespace armnn

// Allows grouping events in an user-defined manner (e.g. per inference)
#define ARMNN_UPDATE_PROFILING_EVENT_TAG() armnn::Profiler::Get().UpdateEventTag();

// The event name must be known at compile time
#define ARMNN_SCOPED_PROFILING_EVENT(compute, name) armnn::ScopedProfilingEvent e_##__FILE__##__LINE__(compute, name);

#else

#define ARMNN_UPDATE_PROFILING_EVENT_TAG()
#define ARMNN_SCOPED_PROFILING_EVENT(compute, name)

#endif // ARMNN_PROFILING_ENABLED