From e2da7ee5e9732ec0d1962b7d74737b8ef5463a9e Mon Sep 17 00:00:00 2001 From: Kshitij Sisodia Date: Mon, 14 Feb 2022 11:22:58 +0000 Subject: MLECO-2970: Moving Profiler out as a CMake target. Profiler is a stand alone static lib that will depend on log and hal targets. Change-Id: Ibbff289c6760982f54ae278d95a054e73db018c8 --- CMakeLists.txt | 22 +- .../platforms/native/build_configuration.cmake | 1 + source/application/profiler/Profiler.cc | 281 --------------------- source/application/profiler/include/Profiler.hpp | 135 ---------- source/profiler/CMakeLists.txt | 40 +++ source/profiler/Profiler.cc | 281 +++++++++++++++++++++ source/profiler/include/Profiler.hpp | 135 ++++++++++ 7 files changed, 466 insertions(+), 429 deletions(-) delete mode 100644 source/application/profiler/Profiler.cc delete mode 100644 source/application/profiler/include/Profiler.hpp create mode 100644 source/profiler/CMakeLists.txt create mode 100644 source/profiler/Profiler.cc create mode 100644 source/profiler/include/Profiler.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 51e21eb..e61e378 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,19 +98,19 @@ add_subdirectory(${SRC_PATH}/math ${CMAKE_BINARY_DIR}/math) # We include the hal target add_subdirectory(${SRC_PATH}/hal ${CMAKE_BINARY_DIR}/hal) -# Include the tensorflow build target -include(${CMAKE_SCRIPTS_DIR}/tensorflow.cmake) - -# Profiler +# Add the profiler target if (NOT DEFINED PROFILER_DIR) - set(PROFILER_DIR ${SRC_PATH}/application/profiler) + set(PROFILER_DIR ${SRC_PATH}/profiler) endif () +add_subdirectory(${PROFILER_DIR} ${CMAKE_BINARY_DIR}/profiler) + +# Include the tensorflow build target +include(${CMAKE_SCRIPTS_DIR}/tensorflow.cmake) # Include directories for application module: set(APPLICATION_INCLUDE_DIRS ${SRC_PATH}/application/tensorflow-lite-micro/include - ${SRC_PATH}/application/main/include - ${PROFILER_DIR}/include) + ${SRC_PATH}/application/main/include) # Source files for application module: file(GLOB_RECURSE SRC_APPLICATION @@ -122,9 +122,6 @@ file(GLOB_RECURSE SRC_APPLICATION "${SRC_PATH}/application/main/**/*.c" "${SRC_PATH}/application/tensorflow-lite-micro/**/*.cc" "${SRC_PATH}/application/tensorflow-lite-micro/*.cc" - "${PROFILER_DIR}/*.cc" - "${PROFILER_DIR}/*.cpp" - "${PROFILER_DIR}/*.c" ) list(FILTER SRC_APPLICATION EXCLUDE REGEX ".*main\\.c.*$") set(SRC_MAIN "${SRC_PATH}/application/main/Main.cc") @@ -228,9 +225,8 @@ foreach(use_case ${USE_CASES}) log arm_math hal - tensorflow-lite-micro - $<$:${APPLICATION_EXTRA_LIBS}> - ) + profiler + tensorflow-lite-micro) add_executable(${TARGET_NAME} ${SRC_MAIN}) diff --git a/scripts/cmake/platforms/native/build_configuration.cmake b/scripts/cmake/platforms/native/build_configuration.cmake index bf6f980..bc91804 100644 --- a/scripts/cmake/platforms/native/build_configuration.cmake +++ b/scripts/cmake/platforms/native/build_configuration.cmake @@ -39,6 +39,7 @@ function(set_platform_global_defaults) target_include_directories(catch2 INTERFACE ${TEST_TPIP_INCLUDE}) + add_dependencies(catch2 catch2-headers) endfunction() diff --git a/source/application/profiler/Profiler.cc b/source/application/profiler/Profiler.cc deleted file mode 100644 index efbc64d..0000000 --- a/source/application/profiler/Profiler.cc +++ /dev/null @@ -1,281 +0,0 @@ -/* - * 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 - -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& 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(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(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 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 */ diff --git a/source/application/profiler/include/Profiler.hpp b/source/application/profiler/include/Profiler.hpp deleted file mode 100644 index 503d805..0000000 --- a/source/application/profiler/include/Profiler.hpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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. - */ -#ifndef APP_PROFILER_HPP -#define APP_PROFILER_HPP - -#include "hal.h" - -#include -#include -#include - -namespace arm { -namespace app { - - /** Statistics for a profiling metric. */ - struct Statistics { - std::string name; - std::string unit; - std::uint64_t total; - double avrg; - std::uint64_t min; - std::uint64_t max; - }; - - /** Profiling results with calculated statistics. */ - struct ProfileResult { - std::string name; - std::uint32_t samplesNum; - std::vector data; - }; - - /** A single profiling unit definition. */ - struct ProfilingUnit { - uint64_t npuCycles = 0; - uint64_t activeNpuCycles = 0; - uint64_t idleNpuCycles = 0; - uint64_t axi0writes = 0; - uint64_t axi0reads = 0; - uint64_t axi1reads = 0; - uint64_t cpuCycles = 0; - time_t time = 0; - }; - - /* A collection of profiling units. */ - using ProfilingSeries = std::vector; - - /* A map for string identifiable profiling series. */ - using ProfilingMap = std::map; - - /** - * @brief A very simple profiler example using the platform timer - * implementation. - */ - class Profiler { - public: - /** - * @brief Constructor for profiler. - * @param[in] platform Pointer to a valid, initialised hal platform. - * @param[in] name A friendly name for this profiler. - **/ - Profiler(hal_platform* platform, const char* name); - - /** Block the default constructor. */ - Profiler() = delete; - - /** Default destructor. */ - ~Profiler() = default; - - /** @brief Start profiling => get starting time-stamp. */ - bool StartProfiling(const char* name = nullptr); - - /** @brief Stop profiling => get the ending time-stamp. */ - bool StopProfiling(); - - /** @brief Stops the profiling and internally resets the - * platform timers. */ - bool StopProfilingAndReset(); - - /** @brief Reset the platform timers. */ - void Reset(); - - /** - * @brief Collects profiling results statistics and resets the profiler. - **/ - void GetAllResultsAndReset(std::vector& results); - - /** - * @brief Prints collected profiling results and resets the profiler. - **/ - void PrintProfilingResult(bool printFullStat = false); - - /** @brief Set the profiler name. */ - void SetName(const char* str); - - private: - ProfilingMap m_series; /* Profiling series map. */ - time_counter m_tstampSt{}; /* Container for a current starting timestamp. */ - time_counter m_tstampEnd{}; /* Container for a current ending timestamp. */ - hal_platform * m_pPlatform = nullptr; /* Platform pointer - to get the timer. */ - - bool m_started = false; /* Indicates profiler has been started. */ - - std::string m_name; /* Name given to this profiler. */ - - /** - * @brief Appends the profiling unit computed by the "start" and - * "end" timestamps to the profiling series identified by - * the name provided. - * @param[in] start Starting time-stamp. - * @param[in] end Ending time-stamp. - * @param[in] name Name for the profiling unit series to be - * appended to. - **/ - void AddProfilingUnit(time_counter start, time_counter end, - const std::string& name); - }; - -} /* namespace app */ -} /* namespace arm */ - -#endif /* APP_PROFILER_HPP */ diff --git a/source/profiler/CMakeLists.txt b/source/profiler/CMakeLists.txt new file mode 100644 index 0000000..f70e86d --- /dev/null +++ b/source/profiler/CMakeLists.txt @@ -0,0 +1,40 @@ +#---------------------------------------------------------------------------- +# 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. +#---------------------------------------------------------------------------- + +####################################################### +# Profiling library # +####################################################### + +project(profiler + DESCRIPTION "Profiling API for applications." + LANGUAGES C CXX) + +add_library(profiler STATIC) + +target_sources(profiler + PRIVATE + Profiler.cc) + +target_include_directories(profiler PUBLIC include) + +# Profiling API depends on the logging interface and the HAL library. +target_link_libraries(profiler PRIVATE log hal) + +message(STATUS "*******************************************************") +message(STATUS "Library : " profiler) +message(STATUS "CMAKE_SYSTEM_PROCESSOR : " ${CMAKE_SYSTEM_PROCESSOR}) +message(STATUS "*******************************************************") diff --git a/source/profiler/Profiler.cc b/source/profiler/Profiler.cc new file mode 100644 index 0000000..efbc64d --- /dev/null +++ b/source/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 "log_macros.h" + +#include + +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& 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(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(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 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 */ diff --git a/source/profiler/include/Profiler.hpp b/source/profiler/include/Profiler.hpp new file mode 100644 index 0000000..503d805 --- /dev/null +++ b/source/profiler/include/Profiler.hpp @@ -0,0 +1,135 @@ +/* + * 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. + */ +#ifndef APP_PROFILER_HPP +#define APP_PROFILER_HPP + +#include "hal.h" + +#include +#include +#include + +namespace arm { +namespace app { + + /** Statistics for a profiling metric. */ + struct Statistics { + std::string name; + std::string unit; + std::uint64_t total; + double avrg; + std::uint64_t min; + std::uint64_t max; + }; + + /** Profiling results with calculated statistics. */ + struct ProfileResult { + std::string name; + std::uint32_t samplesNum; + std::vector data; + }; + + /** A single profiling unit definition. */ + struct ProfilingUnit { + uint64_t npuCycles = 0; + uint64_t activeNpuCycles = 0; + uint64_t idleNpuCycles = 0; + uint64_t axi0writes = 0; + uint64_t axi0reads = 0; + uint64_t axi1reads = 0; + uint64_t cpuCycles = 0; + time_t time = 0; + }; + + /* A collection of profiling units. */ + using ProfilingSeries = std::vector; + + /* A map for string identifiable profiling series. */ + using ProfilingMap = std::map; + + /** + * @brief A very simple profiler example using the platform timer + * implementation. + */ + class Profiler { + public: + /** + * @brief Constructor for profiler. + * @param[in] platform Pointer to a valid, initialised hal platform. + * @param[in] name A friendly name for this profiler. + **/ + Profiler(hal_platform* platform, const char* name); + + /** Block the default constructor. */ + Profiler() = delete; + + /** Default destructor. */ + ~Profiler() = default; + + /** @brief Start profiling => get starting time-stamp. */ + bool StartProfiling(const char* name = nullptr); + + /** @brief Stop profiling => get the ending time-stamp. */ + bool StopProfiling(); + + /** @brief Stops the profiling and internally resets the + * platform timers. */ + bool StopProfilingAndReset(); + + /** @brief Reset the platform timers. */ + void Reset(); + + /** + * @brief Collects profiling results statistics and resets the profiler. + **/ + void GetAllResultsAndReset(std::vector& results); + + /** + * @brief Prints collected profiling results and resets the profiler. + **/ + void PrintProfilingResult(bool printFullStat = false); + + /** @brief Set the profiler name. */ + void SetName(const char* str); + + private: + ProfilingMap m_series; /* Profiling series map. */ + time_counter m_tstampSt{}; /* Container for a current starting timestamp. */ + time_counter m_tstampEnd{}; /* Container for a current ending timestamp. */ + hal_platform * m_pPlatform = nullptr; /* Platform pointer - to get the timer. */ + + bool m_started = false; /* Indicates profiler has been started. */ + + std::string m_name; /* Name given to this profiler. */ + + /** + * @brief Appends the profiling unit computed by the "start" and + * "end" timestamps to the profiling series identified by + * the name provided. + * @param[in] start Starting time-stamp. + * @param[in] end Ending time-stamp. + * @param[in] name Name for the profiling unit series to be + * appended to. + **/ + void AddProfilingUnit(time_counter start, time_counter end, + const std::string& name); + }; + +} /* namespace app */ +} /* namespace arm */ + +#endif /* APP_PROFILER_HPP */ -- cgit v1.2.1