blob: efbc64d4c9be4c623d7aad42459113baf9424e1c [file] [log] [blame]
/*
* 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 "log_macros.h"
#include <cstring>
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 */