blob: f364759827deae4d0377754841f50bdc6a7c093f [file] [log] [blame]
alexander3c798932021-03-26 21:42:19 +00001/*
2 * Copyright (c) 2021 Arm Limited. All rights reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17#include "Profiler.hpp"
18
19#include <cstring>
20#include <string>
21#include <sstream>
22
23namespace arm {
24namespace app {
25
26 template<class T>
27 static void writeStatLine(std::ostringstream& s,
28 const char* desc,
29 T total,
30 uint32_t samples,
31 T min,
32 T max)
33 {
34 s << "\t" << desc << total << " / "
35 << ((double)total / samples) << " / "
36 << min << " / " << max << std::endl;
37 }
38
39 Profiler::Profiler(hal_platform* platform, const char* name = "Unknown")
40 : _m_name(name)
41 {
42 if (platform && platform->inited) {
43 this->_m_pPlatform = platform;
44 this->Reset();
45 } else {
46 printf_err("Profiler %s initialised with invalid platform\n",
47 this->_m_name.c_str());
48 }
49 }
50
51 bool Profiler::StartProfiling(const char* name)
52 {
53 if (name) {
54 this->SetName(name);
55 }
56 if (this->_m_pPlatform && !this->_m_started) {
57 this->_m_pPlatform->timer->reset();
58 this->_m_tstampSt = this->_m_pPlatform->timer->start_profiling();
59 this->_m_started = true;
60 return true;
61 }
62 printf_err("Failed to start profiler %s\n", this->_m_name.c_str());
63 return false;
64 }
65
66 bool Profiler::StopProfiling()
67 {
68 if (this->_m_pPlatform && this->_m_started) {
69 this->_m_tstampEnd = this->_m_pPlatform->timer->stop_profiling();
70 this->_m_started = false;
71
72 this->_AddProfilingUnit(this->_m_tstampSt, this->_m_tstampEnd, this->_m_name);
73
74 return true;
75 }
76 printf_err("Failed to stop profiler %s\n", this->_m_name.c_str());
77 return false;
78 }
79
80 bool Profiler::StopProfilingAndReset()
81 {
82 if (this->StopProfiling()) {
83 this->Reset();
84 return true;
85 }
86 printf_err("Failed to stop profiler %s\n", this->_m_name.c_str());
87 return false;
88 }
89
90 void Profiler::Reset()
91 {
92 this->_m_started = false;
93 memset(&this->_m_tstampSt, 0, sizeof(this->_m_tstampSt));
94 memset(&this->_m_tstampEnd, 0, sizeof(this->_m_tstampEnd));
95 }
96
97 std::string Profiler::GetResultsAndReset()
98 {
99 std::ostringstream strResults;
100
101 for (const auto& item: this->_m_series) {
102 auto name = item.first;
103 ProfilingSeries series = item.second;
104
105 uint32_t samplesNum = series.size();
106
107 uint64_t totalNpuCycles = 0; /* Total NPU cycles (idle + active). */
108 uint64_t totalActiveNpuCycles = 0; /* Active NPU cycles. */
109 uint64_t totalCpuCycles = 0; /* Total CPU cycles. */
110 time_t totalTimeMs = 0;
111
112 uint64_t minActiveNpuCycles = series[0].activeNpuCycles;
113 uint64_t minIdleNpuCycles = series[0].npuCycles - minActiveNpuCycles;
114 uint64_t minActiveCpuCycles = series[0].cpuCycles - minActiveNpuCycles;
115 time_t minTimeMs = series[0].time;
116
117 uint64_t maxIdleNpuCycles = 0;
118 uint64_t maxActiveNpuCycles = 0;
119 uint64_t maxActiveCpuCycles = 0;
120 time_t maxTimeMs = 0;
121
122 for(ProfilingUnit& unit: series){
123 totalNpuCycles += unit.npuCycles;
124 totalActiveNpuCycles += unit.activeNpuCycles;
125 totalCpuCycles += unit.cpuCycles;
126 totalTimeMs += unit.time;
127
128 maxActiveNpuCycles = std::max(maxActiveNpuCycles,
129 unit.activeNpuCycles);
130 maxIdleNpuCycles = std::max(maxIdleNpuCycles,
131 unit.npuCycles - maxActiveNpuCycles);
132 maxActiveCpuCycles = std::max(maxActiveCpuCycles,
133 unit.cpuCycles - maxActiveNpuCycles);
134 maxTimeMs = std::max(maxTimeMs, unit.time);
135
136 minActiveNpuCycles = std::min(minActiveNpuCycles,
137 unit.activeNpuCycles);
138 minIdleNpuCycles = std::min(minIdleNpuCycles,
139 unit.npuCycles - minActiveNpuCycles);
140 minActiveCpuCycles = std::min(minActiveCpuCycles,
141 unit.cpuCycles - minActiveNpuCycles);
142 minTimeMs = std::min(minTimeMs, unit.time);
143 }
144
145 strResults << "Profile for " << name << ": " << std::endl;
146
147 if (samplesNum > 1) {
148 strResults << "\tSamples: " << samplesNum << std::endl;
149 strResults << "\t Total / Avg./ Min / Max"
150 << std::endl;
151
152 writeStatLine<uint64_t>(strResults, "Active NPU cycles: ",
153 totalActiveNpuCycles, samplesNum,
154 minActiveNpuCycles, maxActiveNpuCycles);
155
156 writeStatLine<uint64_t>(strResults, "Idle NPU cycles: ",
157 (totalNpuCycles - totalActiveNpuCycles),
158 samplesNum, minIdleNpuCycles, maxIdleNpuCycles);
159
160#if defined(CPU_PROFILE_ENABLED)
161 writeStatLine<uint64_t>(strResults, "Active CPU cycles (approx): ",
162 (totalCpuCycles - totalActiveNpuCycles),
163 samplesNum, minActiveCpuCycles,
164 maxActiveCpuCycles);
165
166 writeStatLine<time_t>(strResults, "Time in ms: ",
167 totalTimeMs, samplesNum, minTimeMs, maxTimeMs);
168#endif
169 } else {
170 strResults << "\tActive NPU cycles: " << totalActiveNpuCycles
171 << std::endl;
172 strResults << "\tIdle NPU cycles: "
173 << (totalNpuCycles - totalActiveNpuCycles)
174 << std::endl;
175#if defined(CPU_PROFILE_ENABLED)
176 strResults << "\tActive CPU cycles: "
177 << (totalCpuCycles - totalActiveNpuCycles)
178 << " (approx)" << std::endl;
179
180 strResults << "\tTime in ms: " << totalTimeMs << std::endl;
181#endif
182 }
183 }
184 this->Reset();
185 return strResults.str();
186 }
187
188 void Profiler::SetName(const char* str)
189 {
190 this->_m_name = std::string(str);
191 }
192
193 void Profiler::_AddProfilingUnit(time_counter start, time_counter end,
194 const std::string& name)
195 {
196 platform_timer * timer = this->_m_pPlatform->timer;
197
198 struct ProfilingUnit unit;
199
200 if (timer->cap.npu_cycles && timer->get_npu_total_cycle_diff &&
201 timer->get_npu_active_cycle_diff)
202 {
203 unit.npuCycles = timer->get_npu_total_cycle_diff(&start, &end);
204 unit.activeNpuCycles = timer->get_npu_active_cycle_diff(&start, &end);
205 }
206
207 if (timer->cap.cpu_cycles && timer->get_cpu_cycle_diff) {
208 unit.cpuCycles = timer->get_cpu_cycle_diff(&start, &end);
209 }
210
211 if (timer->cap.duration_ms && timer->get_duration_ms) {
212 unit.time = timer->get_duration_ms(&start, &end);
213 }
214
215 this->_m_series[name].emplace_back(unit);
216 }
217
218} /* namespace app */
219} /* namespace arm */