/* * 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 #include #include namespace arm { namespace app { template 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(strResults, "Active NPU cycles: ", totalActiveNpuCycles, samplesNum, minActiveNpuCycles, maxActiveNpuCycles); writeStatLine(strResults, "Idle NPU cycles: ", (totalNpuCycles - totalActiveNpuCycles), samplesNum, minIdleNpuCycles, maxIdleNpuCycles); #if defined(CPU_PROFILE_ENABLED) writeStatLine(strResults, "Active CPU cycles (approx): ", (totalCpuCycles - totalActiveNpuCycles), samplesNum, minActiveCpuCycles, maxActiveCpuCycles); writeStatLine(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 */