blob: f364759827deae4d0377754841f50bdc6a7c093f [file] [log] [blame]
/*
* Copyright (c) 2021 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 <string>
#include <sstream>
namespace arm {
namespace app {
template<class T>
static void writeStatLine(std::ostringstream& s,
const char* desc,
T total,
uint32_t samples,
T min,
T max)
{
s << "\t" << desc << total << " / "
<< ((double)total / samples) << " / "
<< min << " / " << max << std::endl;
}
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;
memset(&this->_m_tstampSt, 0, sizeof(this->_m_tstampSt));
memset(&this->_m_tstampEnd, 0, sizeof(this->_m_tstampEnd));
}
std::string Profiler::GetResultsAndReset()
{
std::ostringstream strResults;
for (const auto& item: this->_m_series) {
auto name = item.first;
ProfilingSeries series = item.second;
uint32_t samplesNum = series.size();
uint64_t totalNpuCycles = 0; /* Total NPU cycles (idle + active). */
uint64_t totalActiveNpuCycles = 0; /* Active NPU cycles. */
uint64_t totalCpuCycles = 0; /* Total CPU cycles. */
time_t totalTimeMs = 0;
uint64_t minActiveNpuCycles = series[0].activeNpuCycles;
uint64_t minIdleNpuCycles = series[0].npuCycles - minActiveNpuCycles;
uint64_t minActiveCpuCycles = series[0].cpuCycles - minActiveNpuCycles;
time_t minTimeMs = series[0].time;
uint64_t maxIdleNpuCycles = 0;
uint64_t maxActiveNpuCycles = 0;
uint64_t maxActiveCpuCycles = 0;
time_t maxTimeMs = 0;
for(ProfilingUnit& unit: series){
totalNpuCycles += unit.npuCycles;
totalActiveNpuCycles += unit.activeNpuCycles;
totalCpuCycles += unit.cpuCycles;
totalTimeMs += unit.time;
maxActiveNpuCycles = std::max(maxActiveNpuCycles,
unit.activeNpuCycles);
maxIdleNpuCycles = std::max(maxIdleNpuCycles,
unit.npuCycles - maxActiveNpuCycles);
maxActiveCpuCycles = std::max(maxActiveCpuCycles,
unit.cpuCycles - maxActiveNpuCycles);
maxTimeMs = std::max(maxTimeMs, unit.time);
minActiveNpuCycles = std::min(minActiveNpuCycles,
unit.activeNpuCycles);
minIdleNpuCycles = std::min(minIdleNpuCycles,
unit.npuCycles - minActiveNpuCycles);
minActiveCpuCycles = std::min(minActiveCpuCycles,
unit.cpuCycles - minActiveNpuCycles);
minTimeMs = std::min(minTimeMs, unit.time);
}
strResults << "Profile for " << name << ": " << std::endl;
if (samplesNum > 1) {
strResults << "\tSamples: " << samplesNum << std::endl;
strResults << "\t Total / Avg./ Min / Max"
<< std::endl;
writeStatLine<uint64_t>(strResults, "Active NPU cycles: ",
totalActiveNpuCycles, samplesNum,
minActiveNpuCycles, maxActiveNpuCycles);
writeStatLine<uint64_t>(strResults, "Idle NPU cycles: ",
(totalNpuCycles - totalActiveNpuCycles),
samplesNum, minIdleNpuCycles, maxIdleNpuCycles);
#if defined(CPU_PROFILE_ENABLED)
writeStatLine<uint64_t>(strResults, "Active CPU cycles (approx): ",
(totalCpuCycles - totalActiveNpuCycles),
samplesNum, minActiveCpuCycles,
maxActiveCpuCycles);
writeStatLine<time_t>(strResults, "Time in ms: ",
totalTimeMs, samplesNum, minTimeMs, maxTimeMs);
#endif
} else {
strResults << "\tActive NPU cycles: " << totalActiveNpuCycles
<< std::endl;
strResults << "\tIdle NPU cycles: "
<< (totalNpuCycles - totalActiveNpuCycles)
<< std::endl;
#if defined(CPU_PROFILE_ENABLED)
strResults << "\tActive CPU cycles: "
<< (totalCpuCycles - totalActiveNpuCycles)
<< " (approx)" << std::endl;
strResults << "\tTime in ms: " << totalTimeMs << std::endl;
#endif
}
}
this->Reset();
return strResults.str();
}
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)
{
platform_timer * timer = this->_m_pPlatform->timer;
struct ProfilingUnit unit;
if (timer->cap.npu_cycles && timer->get_npu_total_cycle_diff &&
timer->get_npu_active_cycle_diff)
{
unit.npuCycles = timer->get_npu_total_cycle_diff(&start, &end);
unit.activeNpuCycles = timer->get_npu_active_cycle_diff(&start, &end);
}
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 */