summaryrefslogtreecommitdiff
path: root/source/application/profiler/Profiler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'source/application/profiler/Profiler.cc')
-rw-r--r--source/application/profiler/Profiler.cc281
1 files changed, 281 insertions, 0 deletions
diff --git a/source/application/profiler/Profiler.cc b/source/application/profiler/Profiler.cc
new file mode 100644
index 0000000..c2b8f2a
--- /dev/null
+++ b/source/application/profiler/Profiler.cc
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2022 Arm Limited. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "Profiler.hpp"
+
+#include <cstring>
+#include <iomanip>
+
+namespace arm {
+namespace app {
+ Profiler::Profiler(hal_platform* platform, const char* name = "Unknown")
+ : m_name(name)
+ {
+ if (platform && platform->inited) {
+ this->m_pPlatform = platform;
+ this->Reset();
+ } else {
+ printf_err("Profiler %s initialised with invalid platform\n",
+ this->m_name.c_str());
+ }
+ }
+
+ bool Profiler::StartProfiling(const char* name)
+ {
+ if (name) {
+ this->SetName(name);
+ }
+ if (this->m_pPlatform && !this->m_started) {
+ this->m_pPlatform->timer->reset();
+ this->m_tstampSt = this->m_pPlatform->timer->start_profiling();
+ this->m_started = true;
+ return true;
+ }
+ printf_err("Failed to start profiler %s\n", this->m_name.c_str());
+ return false;
+ }
+
+ bool Profiler::StopProfiling()
+ {
+ if (this->m_pPlatform && this->m_started) {
+ this->m_tstampEnd = this->m_pPlatform->timer->stop_profiling();
+ this->m_started = false;
+
+ this->AddProfilingUnit(this->m_tstampSt, this->m_tstampEnd, this->m_name);
+
+ return true;
+ }
+ printf_err("Failed to stop profiler %s\n", this->m_name.c_str());
+ return false;
+ }
+
+ bool Profiler::StopProfilingAndReset()
+ {
+ if (this->StopProfiling()) {
+ this->Reset();
+ return true;
+ }
+ printf_err("Failed to stop profiler %s\n", this->m_name.c_str());
+ return false;
+ }
+
+ void Profiler::Reset()
+ {
+ this->m_started = false;
+ this->m_series.clear();
+ memset(&this->m_tstampSt, 0, sizeof(this->m_tstampSt));
+ memset(&this->m_tstampEnd, 0, sizeof(this->m_tstampEnd));
+ }
+
+ void calcProfilingStat(uint64_t currentValue,
+ Statistics& data,
+ uint32_t samples)
+ {
+ data.total += currentValue;
+ data.min = std::min(data.min, currentValue);
+ data.max = std::max(data.max, currentValue);
+ data.avrg = ((double)data.total / samples);
+ }
+
+ void Profiler::GetAllResultsAndReset(std::vector<ProfileResult>& results)
+ {
+ for (const auto& item: this->m_series) {
+ auto name = item.first;
+ ProfilingSeries series = item.second;
+ ProfileResult result{};
+ result.name = item.first;
+ result.samplesNum = series.size();
+
+ Statistics AXI0_RD {
+ .name = "NPU AXI0_RD_DATA_BEAT_RECEIVED",
+ .unit = "beats",
+ .total = 0,
+ .avrg = 0.0,
+ .min = series[0].axi0writes,
+ .max = 0
+ };
+ Statistics AXI0_WR {
+ .name = "NPU AXI0_WR_DATA_BEAT_WRITTEN",
+ .unit = "beats",
+ .total = 0,
+ .avrg = 0.0,
+ .min = series[0].axi0reads,
+ .max = 0
+ };
+ Statistics AXI1_RD {
+ .name = "NPU AXI1_RD_DATA_BEAT_RECEIVED",
+ .unit = "beats",
+ .total = 0,
+ .avrg = 0.0,
+ .min = series[0].axi1reads,
+ .max = 0
+ };
+ Statistics NPU_ACTIVE {
+ .name = "NPU ACTIVE",
+ .unit = "cycles",
+ .total = 0,
+ .avrg = 0.0,
+ .min = series[0].activeNpuCycles,
+ .max = 0
+ };
+ Statistics NPU_IDLE {
+ .name = "NPU IDLE",
+ .unit = "cycles",
+ .total = 0,
+ .avrg = 0.0,
+ .min = series[0].idleNpuCycles,
+ .max = 0
+ };
+ Statistics NPU_Total {
+ .name = "NPU TOTAL",
+ .unit = "cycles",
+ .total = 0,
+ .avrg = 0.0,
+ .min = series[0].npuCycles,
+ .max = 0,
+ };
+#if defined(CPU_PROFILE_ENABLED)
+ Statistics CPU_ACTIVE {
+ .name = "CPU ACTIVE",
+ .unit = "cycles (approx)",
+ .total = 0,
+ .avrg = 0.0,
+ .min = series[0].cpuCycles - NPU_ACTIVE.min,
+ .max = 0
+ };
+ Statistics TIME {
+ .name = "Time",
+ .unit = "ms",
+ .total = 0,
+ .avrg = 0.0,
+ .min = static_cast<uint64_t>(series[0].time),
+ .max = 0
+ };
+#endif
+ for(ProfilingUnit& unit: series){
+
+ calcProfilingStat(unit.npuCycles,
+ NPU_Total, result.samplesNum);
+
+ calcProfilingStat(unit.activeNpuCycles,
+ NPU_ACTIVE, result.samplesNum);
+
+ calcProfilingStat(unit.idleNpuCycles,
+ NPU_IDLE, result.samplesNum);
+
+ calcProfilingStat(unit.axi0writes,
+ AXI0_WR, result.samplesNum);
+
+ calcProfilingStat(unit.axi0reads,
+ AXI0_RD, result.samplesNum);
+
+ calcProfilingStat(unit.axi1reads,
+ AXI1_RD, result.samplesNum);
+#if defined(CPU_PROFILE_ENABLED)
+ calcProfilingStat(static_cast<uint64_t>(unit.time),
+ TIME, result.samplesNum);
+
+ calcProfilingStat(unit.cpuCycles - unit.activeNpuCycles,
+ CPU_ACTIVE, result.samplesNum);
+#endif
+ }
+ result.data.emplace_back(AXI0_RD);
+ result.data.emplace_back(AXI0_WR);
+ result.data.emplace_back(AXI1_RD);
+ result.data.emplace_back(NPU_ACTIVE);
+ result.data.emplace_back(NPU_IDLE);
+ result.data.emplace_back(NPU_Total);
+#if defined(CPU_PROFILE_ENABLED)
+ result.data.emplace_back(CPU_ACTIVE);
+ result.data.emplace_back(TIME);
+#endif
+ results.emplace_back(result);
+ }
+ this->Reset();
+ }
+
+ void printStatisticsHeader(uint32_t samplesNum) {
+ info("Number of samples: %" PRIu32 "\n", samplesNum);
+ info("%s\n", "Total / Avg./ Min / Max");
+ }
+
+ void Profiler::PrintProfilingResult(bool printFullStat) {
+ std::vector<ProfileResult> results{};
+ GetAllResultsAndReset(results);
+ for(ProfileResult& result: results) {
+ info("Profile for %s:\n", result.name.c_str());
+
+ if (printFullStat) {
+ printStatisticsHeader(result.samplesNum);
+ }
+
+ for (Statistics &stat: result.data) {
+ if (printFullStat) {
+ info("%s %s: %" PRIu64 "/ %.0f / %" PRIu64 " / %" PRIu64 " \n",
+ stat.name.c_str(), stat.unit.c_str(),
+ stat.total, stat.avrg, stat.min, stat.max);
+ } else {
+ info("%s %s: %.0f\n", stat.name.c_str(), stat.unit.c_str(), stat.avrg);
+ }
+ }
+ }
+ }
+
+ void Profiler::SetName(const char* str)
+ {
+ this->m_name = std::string(str);
+ }
+
+ void Profiler::AddProfilingUnit(time_counter start, time_counter end,
+ const std::string& name)
+ {
+ if (!this->m_pPlatform) {
+ printf_err("Invalid platform\n");
+ return;
+ }
+
+ platform_timer * timer = this->m_pPlatform->timer;
+
+ struct ProfilingUnit unit;
+
+ if (timer->cap.npu_cycles && timer->get_npu_cycles_diff)
+ {
+ const size_t size = 6;
+ uint64_t pmuCounters[size] = {0};
+ /* 6 values: total cc, active cc, idle cc, axi0 read, axi0 write, axi1 read*/
+ if (0 == timer->get_npu_cycles_diff(&start, &end, pmuCounters, size)) {
+ unit.npuCycles = pmuCounters[0];
+ unit.activeNpuCycles = pmuCounters[1];
+ unit.idleNpuCycles = pmuCounters[2];
+ unit.axi0reads = pmuCounters[3];
+ unit.axi0writes = pmuCounters[4];
+ unit.axi1reads = pmuCounters[5];
+ }
+ }
+
+ if (timer->cap.cpu_cycles && timer->get_cpu_cycle_diff) {
+ unit.cpuCycles = timer->get_cpu_cycle_diff(&start, &end);
+ }
+
+ if (timer->cap.duration_ms && timer->get_duration_ms) {
+ unit.time = timer->get_duration_ms(&start, &end);
+ }
+
+ this->m_series[name].emplace_back(unit);
+ }
+
+} /* namespace app */
+} /* namespace arm */