diff options
Diffstat (limited to 'python/openvino/runtime/common/monitors')
9 files changed, 932 insertions, 0 deletions
diff --git a/python/openvino/runtime/common/monitors/CMakeLists.txt b/python/openvino/runtime/common/monitors/CMakeLists.txt new file mode 100644 index 0000000..1bfe0b9 --- /dev/null +++ b/python/openvino/runtime/common/monitors/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2018-2019 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +find_package(OpenCV REQUIRED COMPONENTS core imgproc) + +set(SOURCES + src/cpu_monitor.cpp + src/memory_monitor.cpp + src/presenter.cpp) + +set(HEADERS + include/monitors/cpu_monitor.h + include/monitors/memory_monitor.h + include/monitors/presenter.h) + +if(WIN32) + list(APPEND SOURCES src/query_wrapper.cpp) + list(APPEND HEADERS include/monitors/query_wrapper.h) +endif() +# Create named folders for the sources within the .vcproj +# Empty name lists them directly under the .vcproj +source_group("src" FILES ${SOURCES}) +source_group("include" FILES ${HEADERS}) + +add_library(monitors STATIC ${SOURCES} ${HEADERS}) +target_include_directories(monitors PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(monitors PRIVATE opencv_core opencv_imgproc) +if(WIN32) + target_link_libraries(monitors PRIVATE pdh) + + target_compile_definitions(monitors PRIVATE + # Prevents Windows.h from adding unnecessary includes + WIN32_LEAN_AND_MEAN + # Prevents Windows.h from defining min/max as macros + NOMINMAX + ) +endif() diff --git a/python/openvino/runtime/common/monitors/include/monitors/cpu_monitor.h b/python/openvino/runtime/common/monitors/include/monitors/cpu_monitor.h new file mode 100644 index 0000000..38d2845 --- /dev/null +++ b/python/openvino/runtime/common/monitors/include/monitors/cpu_monitor.h @@ -0,0 +1,28 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include <deque> +#include <memory> +#include <vector> + +class CpuMonitor { +public: + CpuMonitor(); + ~CpuMonitor(); + void setHistorySize(std::size_t size); + std::size_t getHistorySize() const; + void collectData(); + std::deque<std::vector<double>> getLastHistory() const; + std::vector<double> getMeanCpuLoad() const; + +private: + unsigned samplesNumber; + unsigned historySize; + std::vector<double> cpuLoadSum; + std::deque<std::vector<double>> cpuLoadHistory; + class PerformanceCounter; + std::unique_ptr<PerformanceCounter> performanceCounter; +}; diff --git a/python/openvino/runtime/common/monitors/include/monitors/memory_monitor.h b/python/openvino/runtime/common/monitors/include/monitors/memory_monitor.h new file mode 100644 index 0000000..9eda10f --- /dev/null +++ b/python/openvino/runtime/common/monitors/include/monitors/memory_monitor.h @@ -0,0 +1,34 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include <deque> +#include <memory> + +class MemoryMonitor { +public: + MemoryMonitor(); + ~MemoryMonitor(); + void setHistorySize(std::size_t size); + std::size_t getHistorySize() const; + void collectData(); + std::deque<std::pair<double, double>> getLastHistory() const; + double getMeanMem() const; // in GiB + double getMeanSwap() const; + double getMaxMem() const; + double getMaxSwap() const; + double getMemTotal() const; + double getMaxMemTotal() const; // a system may have hotpluggable memory +private: + unsigned samplesNumber; + std::size_t historySize; + double memSum, swapSum; + double maxMem, maxSwap; + double memTotal; + double maxMemTotal; + std::deque<std::pair<double, double>> memSwapUsageHistory; + class PerformanceCounter; + std::unique_ptr<PerformanceCounter> performanceCounter; +}; diff --git a/python/openvino/runtime/common/monitors/include/monitors/presenter.h b/python/openvino/runtime/common/monitors/include/monitors/presenter.h new file mode 100644 index 0000000..c6587a0 --- /dev/null +++ b/python/openvino/runtime/common/monitors/include/monitors/presenter.h @@ -0,0 +1,44 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include <chrono> +#include <map> +#include <ostream> +#include <set> + +#include <opencv2/imgproc.hpp> + +#include "cpu_monitor.h" +#include "memory_monitor.h" + +enum class MonitorType{CpuAverage, DistributionCpu, Memory}; + +class Presenter { +public: + explicit Presenter(std::set<MonitorType> enabledMonitors = {}, + int yPos = 20, + cv::Size graphSize = {150, 60}, + std::size_t historySize = 20); + explicit Presenter(const std::string& keys, + int yPos = 20, + cv::Size graphSize = {150, 60}, + std::size_t historySize = 20); + void addRemoveMonitor(MonitorType monitor); + void handleKey(int key); // handles C, D, M, H keys + void drawGraphs(cv::Mat& frame); + std::vector<std::string> reportMeans() const; + + const int yPos; + const cv::Size graphSize; + const int graphPadding; +private: + std::chrono::steady_clock::time_point prevTimeStamp; + std::size_t historySize; + CpuMonitor cpuMonitor; + bool distributionCpuEnabled; + MemoryMonitor memoryMonitor; + std::ostringstream strStream; +}; diff --git a/python/openvino/runtime/common/monitors/include/monitors/query_wrapper.h b/python/openvino/runtime/common/monitors/include/monitors/query_wrapper.h new file mode 100644 index 0000000..d69f548 --- /dev/null +++ b/python/openvino/runtime/common/monitors/include/monitors/query_wrapper.h @@ -0,0 +1,17 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include <Pdh.h> +class QueryWrapper { +public: + QueryWrapper(); + ~QueryWrapper(); + QueryWrapper(const QueryWrapper&) = delete; + QueryWrapper& operator=(const QueryWrapper&) = delete; + operator PDH_HQUERY() const; +private: + PDH_HQUERY query; +}; diff --git a/python/openvino/runtime/common/monitors/src/cpu_monitor.cpp b/python/openvino/runtime/common/monitors/src/cpu_monitor.cpp new file mode 100644 index 0000000..e5172a2 --- /dev/null +++ b/python/openvino/runtime/common/monitors/src/cpu_monitor.cpp @@ -0,0 +1,206 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "monitors/cpu_monitor.h" + +#include <algorithm> +#ifdef _WIN32 +#include "monitors/query_wrapper.h" +#include <string> +#include <system_error> +#include <PdhMsg.h> +#include <Windows.h> + +namespace { +const std::size_t nCores = []() { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; + }(); +} + +class CpuMonitor::PerformanceCounter { +public: + PerformanceCounter() : coreTimeCounters(nCores) { + PDH_STATUS status; + for (std::size_t i = 0; i < nCores; ++i) { + std::wstring fullCounterPath{L"\\Processor(" + std::to_wstring(i) + L")\\% Processor Time"}; + status = PdhAddCounterW(query, fullCounterPath.c_str(), 0, &coreTimeCounters[i]); + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhAddCounterW() failed"); + } + status = PdhSetCounterScaleFactor(coreTimeCounters[i], -2); // scale counter to [0, 1] + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhSetCounterScaleFactor() failed"); + } + } + status = PdhCollectQueryData(query); + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed"); + } + } + + std::vector<double> getCpuLoad() { + PDH_STATUS status; + status = PdhCollectQueryData(query); + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed"); + } + + PDH_FMT_COUNTERVALUE displayValue; + std::vector<double> cpuLoad(coreTimeCounters.size()); + for (std::size_t i = 0; i < coreTimeCounters.size(); ++i) { + status = PdhGetFormattedCounterValue(coreTimeCounters[i], PDH_FMT_DOUBLE, NULL, + &displayValue); + switch (status) { + case ERROR_SUCCESS: break; + // PdhGetFormattedCounterValue() can sometimes return PDH_CALC_NEGATIVE_DENOMINATOR for some reason + case PDH_CALC_NEGATIVE_DENOMINATOR: return {}; + default: + throw std::system_error(status, std::system_category(), "PdhGetFormattedCounterValue() failed"); + } + if (PDH_CSTATUS_VALID_DATA != displayValue.CStatus && PDH_CSTATUS_NEW_DATA != displayValue.CStatus) { + throw std::runtime_error("Error in counter data"); + } + + cpuLoad[i] = displayValue.doubleValue; + } + return cpuLoad; + } + +private: + QueryWrapper query; + std::vector<PDH_HCOUNTER> coreTimeCounters; +}; + +#elif __linux__ +#include <chrono> +#include <regex> +#include <utility> +#include <fstream> +#include <unistd.h> + +namespace { +const long clockTicks = sysconf(_SC_CLK_TCK); + +const std::size_t nCores = sysconf(_SC_NPROCESSORS_CONF); + +std::vector<unsigned long> getIdleCpuStat() { + std::vector<unsigned long> idleCpuStat(nCores); + std::ifstream procStat("/proc/stat"); + std::string line; + std::smatch match; + std::regex coreJiffies("^cpu(\\d+)\\s+" + "(\\d+)\\s+" + "(\\d+)\\s+" + "(\\d+)\\s+" + "(\\d+)\\s+" // idle + "(\\d+)"); // iowait + + while (std::getline(procStat, line)) { + if (std::regex_search(line, match, coreJiffies)) { + // it doesn't handle overflow of sum and overflows of /proc/stat values + unsigned long idleInfo = stoul(match[5]) + stoul(match[6]), + coreId = stoul(match[1]); + if (nCores <= coreId) { + throw std::runtime_error("The number of cores has changed"); + } + idleCpuStat[coreId] = idleInfo; + } + } + return idleCpuStat; +} +} + +class CpuMonitor::PerformanceCounter { +public: + PerformanceCounter() : prevIdleCpuStat{getIdleCpuStat()}, prevTimePoint{std::chrono::steady_clock::now()} {} + + std::vector<double> getCpuLoad() { + std::vector<unsigned long> idleCpuStat = getIdleCpuStat(); + auto timePoint = std::chrono::steady_clock::now(); + // don't update data too frequently which may result in negative values for cpuLoad. + // It may happen when collectData() is called just after setHistorySize(). + if (timePoint - prevTimePoint > std::chrono::milliseconds{100}) { + std::vector<double> cpuLoad(nCores); + for (std::size_t i = 0; i < idleCpuStat.size(); ++i) { + double idleDiff = idleCpuStat[i] - prevIdleCpuStat[i]; + typedef std::chrono::duration<double, std::chrono::seconds::period> Sec; + cpuLoad[i] = 1.0 + - idleDiff / clockTicks / std::chrono::duration_cast<Sec>(timePoint - prevTimePoint).count(); + } + prevIdleCpuStat = std::move(idleCpuStat); + prevTimePoint = timePoint; + return cpuLoad; + } + return {}; + } +private: + std::vector<unsigned long> prevIdleCpuStat; + std::chrono::steady_clock::time_point prevTimePoint; +}; + +#else +// not implemented +namespace { +const std::size_t nCores{0}; +} + +class CpuMonitor::PerformanceCounter { +public: + std::vector<double> getCpuLoad() {return {};}; +}; +#endif + +CpuMonitor::CpuMonitor() : + samplesNumber{0}, + historySize{0}, + cpuLoadSum(nCores, 0) {} + +// PerformanceCounter is incomplete in header and destructor can't be defined implicitly +CpuMonitor::~CpuMonitor() = default; + +void CpuMonitor::setHistorySize(std::size_t size) { + if (0 == historySize && 0 != size) { + performanceCounter.reset(new PerformanceCounter); + } else if (0 != historySize && 0 == size) { + performanceCounter.reset(); + } + historySize = size; + std::ptrdiff_t newSize = static_cast<std::ptrdiff_t>(std::min(size, cpuLoadHistory.size())); + cpuLoadHistory.erase(cpuLoadHistory.begin(), cpuLoadHistory.end() - newSize); +} + +void CpuMonitor::collectData() { + std::vector<double> cpuLoad = performanceCounter->getCpuLoad(); + + if (!cpuLoad.empty()) { + for (std::size_t i = 0; i < cpuLoad.size(); ++i) { + cpuLoadSum[i] += cpuLoad[i]; + } + ++samplesNumber; + + cpuLoadHistory.push_back(std::move(cpuLoad)); + if (cpuLoadHistory.size() > historySize) { + cpuLoadHistory.pop_front(); + } + } +} + +std::size_t CpuMonitor::getHistorySize() const { + return historySize; +} + +std::deque<std::vector<double>> CpuMonitor::getLastHistory() const { + return cpuLoadHistory; +} + +std::vector<double> CpuMonitor::getMeanCpuLoad() const { + std::vector<double> meanCpuLoad; + meanCpuLoad.reserve(cpuLoadSum.size()); + for (double coreLoad : cpuLoadSum) { + meanCpuLoad.push_back(samplesNumber ? coreLoad / samplesNumber : 0); + } + return meanCpuLoad; +} diff --git a/python/openvino/runtime/common/monitors/src/memory_monitor.cpp b/python/openvino/runtime/common/monitors/src/memory_monitor.cpp new file mode 100644 index 0000000..70879d6 --- /dev/null +++ b/python/openvino/runtime/common/monitors/src/memory_monitor.cpp @@ -0,0 +1,213 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "monitors/memory_monitor.h" + +struct MemState { + double memTotal, usedMem, usedSwap; +}; + +#ifdef _WIN32 +#include "monitors/query_wrapper.h" +#include <algorithm> +#define PSAPI_VERSION 2 +#include <system_error> +#include <Windows.h> +#include <PdhMsg.h> +#include <Psapi.h> + +namespace { +double getMemTotal() { + PERFORMANCE_INFORMATION performanceInformation; + if (!GetPerformanceInfo(&performanceInformation, sizeof(performanceInformation))) { + throw std::runtime_error("GetPerformanceInfo() failed"); + } + return static_cast<double>(performanceInformation.PhysicalTotal * performanceInformation.PageSize) + / (1024 * 1024 * 1024); +} +} + +class MemoryMonitor::PerformanceCounter { +public: + PerformanceCounter() { + PDH_STATUS status = PdhAddCounterW(query, L"\\Paging File(_Total)\\% Usage", 0, &pagingFileUsageCounter); + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhAddCounterW() failed"); + } + status = PdhSetCounterScaleFactor(pagingFileUsageCounter, -2); // scale counter to [0, 1] + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhSetCounterScaleFactor() failed"); + } + } + + MemState getMemState() { + PERFORMANCE_INFORMATION performanceInformation; + if (!GetPerformanceInfo(&performanceInformation, sizeof(performanceInformation))) { + throw std::runtime_error("GetPerformanceInfo() failed"); + } + + PDH_STATUS status; + status = PdhCollectQueryData(query); + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed"); + } + PDH_FMT_COUNTERVALUE displayValue; + status = PdhGetFormattedCounterValue(pagingFileUsageCounter, PDH_FMT_DOUBLE, NULL, &displayValue); + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhGetFormattedCounterValue() failed"); + } + if (PDH_CSTATUS_VALID_DATA != displayValue.CStatus && PDH_CSTATUS_NEW_DATA != displayValue.CStatus) { + throw std::runtime_error("Error in counter data"); + } + + double pagingFilesSize = static_cast<double>( + (performanceInformation.CommitLimit - performanceInformation.PhysicalTotal) + * performanceInformation.PageSize) / (1024 * 1024 * 1024); + return {static_cast<double>(performanceInformation.PhysicalTotal * performanceInformation.PageSize) + / (1024 * 1024 * 1024), + static_cast<double>( + (performanceInformation.PhysicalTotal - performanceInformation.PhysicalAvailable) + * performanceInformation.PageSize) / (1024 * 1024 * 1024), + pagingFilesSize * displayValue.doubleValue}; + } +private: + QueryWrapper query; + PDH_HCOUNTER pagingFileUsageCounter; +}; + +#elif __linux__ +#include <fstream> +#include <utility> +#include <vector> +#include <regex> + +namespace { +std::pair<std::pair<double, double>, std::pair<double, double>> getAvailableMemSwapTotalMemSwap() { + double memAvailable = 0, swapFree = 0, memTotal = 0, swapTotal = 0; + std::regex memRegex("^(.+):\\s+(\\d+) kB$"); + std::string line; + std::smatch match; + std::ifstream meminfo("/proc/meminfo"); + while (std::getline(meminfo, line)) { + if (std::regex_match(line, match, memRegex)) { + if ("MemAvailable" == match[1]) { + memAvailable = stod(match[2]) / (1024 * 1024); + } else if ("SwapFree" == match[1]) { + swapFree = stod(match[2]) / (1024 * 1024); + } else if ("MemTotal" == match[1]) { + memTotal = stod(match[2]) / (1024 * 1024); + } else if ("SwapTotal" == match[1]) { + swapTotal = stod(match[2]) / (1024 * 1024); + } + } + } + if (0 == memTotal) { + throw std::runtime_error("Can't get MemTotal"); + } + return {{memAvailable, swapFree}, {memTotal, swapTotal}}; +} + +double getMemTotal() { + return getAvailableMemSwapTotalMemSwap().second.first; +} +} + +class MemoryMonitor::PerformanceCounter { +public: + MemState getMemState() { + std::pair<std::pair<double, double>, std::pair<double, double>> availableMemSwapTotalMemSwap + = getAvailableMemSwapTotalMemSwap(); + double memTotal = availableMemSwapTotalMemSwap.second.first; + double swapTotal = availableMemSwapTotalMemSwap.second.second; + return {memTotal, memTotal - availableMemSwapTotalMemSwap.first.first, swapTotal - availableMemSwapTotalMemSwap.first.second}; + } +}; + +#else +// not implemented +namespace { +double getMemTotal() {return 0.0;} +} + +class MemoryMonitor::PerformanceCounter { +public: + MemState getMemState() {return {0.0, 0.0, 0.0};} +}; +#endif + +MemoryMonitor::MemoryMonitor() : + samplesNumber{0}, + historySize{0}, + memSum{0.0}, + swapSum{0.0}, + maxMem{0.0}, + maxSwap{0.0}, + memTotal{0.0}, + maxMemTotal{0.0} {} + +// PerformanceCounter is incomplete in header and destructor can't be defined implicitly +MemoryMonitor::~MemoryMonitor() = default; + +void MemoryMonitor::setHistorySize(std::size_t size) { + if (0 == historySize && 0 != size) { + performanceCounter.reset(new MemoryMonitor::PerformanceCounter); + // memTotal is not initialized in constructor because for linux its initialization involves constructing + // std::regex which is unimplemented and throws an exception for gcc 4.8.5 (default for CentOS 7.4). + // Delaying initialization triggers the error only when the monitor is used + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53631 + memTotal = ::getMemTotal(); + } else if (0 != historySize && 0 == size) { + performanceCounter.reset(); + } + historySize = size; + std::size_t newSize = std::min(size, memSwapUsageHistory.size()); + memSwapUsageHistory.erase(memSwapUsageHistory.begin(), memSwapUsageHistory.end() - newSize); +} + +void MemoryMonitor::collectData() { + MemState memState = performanceCounter->getMemState(); + maxMemTotal = std::max(maxMemTotal, memState.memTotal); + memSum += memState.usedMem; + swapSum += memState.usedSwap; + ++samplesNumber; + maxMem = std::max(maxMem, memState.usedMem); + maxSwap = std::max(maxSwap, memState.usedSwap); + + memSwapUsageHistory.emplace_back(memState.usedMem, memState.usedSwap); + if (memSwapUsageHistory.size() > historySize) { + memSwapUsageHistory.pop_front(); + } +} + +std::size_t MemoryMonitor::getHistorySize() const { + return historySize; +} + +std::deque<std::pair<double, double>> MemoryMonitor::getLastHistory() const { + return memSwapUsageHistory; +} + +double MemoryMonitor::getMeanMem() const { + return samplesNumber ? memSum / samplesNumber : 0; +} + +double MemoryMonitor::getMeanSwap() const { + return samplesNumber ? swapSum / samplesNumber : 0; +} + +double MemoryMonitor::getMaxMem() const { + return maxMem; +} + +double MemoryMonitor::getMaxSwap() const { + return maxSwap; +} + +double MemoryMonitor::getMemTotal() const { + return memTotal; +} + +double MemoryMonitor::getMaxMemTotal() const { + return maxMemTotal; +} diff --git a/python/openvino/runtime/common/monitors/src/presenter.cpp b/python/openvino/runtime/common/monitors/src/presenter.cpp new file mode 100644 index 0000000..61f5e15 --- /dev/null +++ b/python/openvino/runtime/common/monitors/src/presenter.cpp @@ -0,0 +1,330 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include <cctype> +#include <chrono> +#include <iomanip> +#include <numeric> +#include <string> +#include <vector> + +#include "monitors/presenter.h" + +namespace { +const std::map<int, MonitorType> keyToMonitorType{ + {'C', MonitorType::CpuAverage}, + {'D', MonitorType::DistributionCpu}, + {'M', MonitorType::Memory}}; + +std::set<MonitorType> strKeysToMonitorSet(const std::string& keys) { + std::set<MonitorType> enabledMonitors; + if (keys == "h") { + return enabledMonitors; + } + for (unsigned char key: keys) { + if (key == 'h') { + throw std::runtime_error("Unacceptable combination of monitor types-can't show and hide info at the same time"); + } + auto iter = keyToMonitorType.find(std::toupper(key)); + if (keyToMonitorType.end() == iter) { + throw std::runtime_error("Unknown monitor type"); + } else { + enabledMonitors.insert(iter->second); + } + } + return enabledMonitors; +} +} + +Presenter::Presenter(std::set<MonitorType> enabledMonitors, + int yPos, + cv::Size graphSize, + std::size_t historySize) : + yPos{yPos}, + graphSize{graphSize}, + graphPadding{std::max(1, static_cast<int>(graphSize.width * 0.05))}, + historySize{historySize}, + distributionCpuEnabled{false}, + strStream{std::ios_base::app} { + for (MonitorType monitor : enabledMonitors) { + addRemoveMonitor(monitor); + } +} + +Presenter::Presenter(const std::string& keys, int yPos, cv::Size graphSize, std::size_t historySize) : + Presenter{strKeysToMonitorSet(keys), yPos, graphSize, historySize} {} + +void Presenter::addRemoveMonitor(MonitorType monitor) { + unsigned updatedHistorySize = 1; + if (historySize > 1) { + int sampleStep = std::max(1, static_cast<int>(graphSize.width / (historySize - 1))); + // +1 to plot graphSize.width/sampleStep segments + // add round up to and an extra element if don't reach graph edge + updatedHistorySize = (graphSize.width + sampleStep - 1) / sampleStep + 1; + } + switch(monitor) { + case MonitorType::CpuAverage: { + if (cpuMonitor.getHistorySize() > 1 && distributionCpuEnabled) { + cpuMonitor.setHistorySize(1); + } else if (cpuMonitor.getHistorySize() > 1 && !distributionCpuEnabled) { + cpuMonitor.setHistorySize(0); + } else { // cpuMonitor.getHistorySize() <= 1 + cpuMonitor.setHistorySize(updatedHistorySize); + } + break; + } + case MonitorType::DistributionCpu: { + if (distributionCpuEnabled) { + distributionCpuEnabled = false; + if (1 == cpuMonitor.getHistorySize()) { // cpuMonitor was used only for DistributionCpu => disable it + cpuMonitor.setHistorySize(0); + } + } else { + distributionCpuEnabled = true; + cpuMonitor.setHistorySize(std::max(std::size_t{1}, cpuMonitor.getHistorySize())); + } + break; + } + case MonitorType::Memory: { + if (memoryMonitor.getHistorySize() > 1) { + memoryMonitor.setHistorySize(0); + } else { + memoryMonitor.setHistorySize(updatedHistorySize); + } + break; + } + } +} + +void Presenter::handleKey(int key) { + key = std::toupper(key); + if ('H' == key) { + if (0 == cpuMonitor.getHistorySize() && memoryMonitor.getHistorySize() <= 1) { + addRemoveMonitor(MonitorType::CpuAverage); + addRemoveMonitor(MonitorType::DistributionCpu); + addRemoveMonitor(MonitorType::Memory); + } else { + cpuMonitor.setHistorySize(0); + distributionCpuEnabled = false; + memoryMonitor.setHistorySize(0); + } + } else { + auto iter = keyToMonitorType.find(key); + if (keyToMonitorType.end() != iter) { + addRemoveMonitor(iter->second); + } + } +} + +void Presenter::drawGraphs(cv::Mat& frame) { + const std::chrono::steady_clock::time_point curTimeStamp = std::chrono::steady_clock::now(); + if (curTimeStamp - prevTimeStamp >= std::chrono::milliseconds{1000}) { + prevTimeStamp = curTimeStamp; + if (0 != cpuMonitor.getHistorySize()) { + cpuMonitor.collectData(); + } + if (memoryMonitor.getHistorySize() > 1) { + memoryMonitor.collectData(); + } + } + + int numberOfEnabledMonitors = (cpuMonitor.getHistorySize() > 1) + distributionCpuEnabled + + (memoryMonitor.getHistorySize() > 1); + int panelWidth = graphSize.width * numberOfEnabledMonitors + + std::max(0, numberOfEnabledMonitors - 1) * graphPadding; + while (panelWidth > frame.cols) { + panelWidth = std::max(0, panelWidth - graphSize.width - graphPadding); + --numberOfEnabledMonitors; // can't draw all monitors + } + int graphPos = std::max(0, (frame.cols - 1 - panelWidth) / 2); + int textGraphSplittingLine = graphSize.height / 5; + int graphRectHeight = graphSize.height - textGraphSplittingLine; + int sampleStep = 1; + unsigned possibleHistorySize = 1; + if (historySize > 1) { + sampleStep = std::max(1, static_cast<int>(graphSize.width / (historySize - 1))); + possibleHistorySize = (graphSize.width + sampleStep - 1) / sampleStep + 1; + } + + if (cpuMonitor.getHistorySize() > 1 && possibleHistorySize > 1 && --numberOfEnabledMonitors >= 0) { + std::deque<std::vector<double>> lastHistory = cpuMonitor.getLastHistory(); + cv::Rect intersection = cv::Rect{cv::Point(graphPos, yPos), graphSize} & cv::Rect{0, 0, frame.cols, frame.rows}; + if (!intersection.area()) { + return; + } + cv::Mat graph = frame(intersection); + graph = graph / 2 + cv::Scalar{127, 127, 127}; + + int lineXPos = graph.cols - 1; + std::vector<cv::Point> averageLoad(lastHistory.size()); + + for (int i = lastHistory.size() - 1; i >= 0; --i) { + double mean = std::accumulate(lastHistory[i].begin(), lastHistory[i].end(), 0.0) / lastHistory[i].size(); + averageLoad[i] = {lineXPos, graphSize.height - static_cast<int>(mean * graphRectHeight)}; + lineXPos -= sampleStep; + } + + cv::polylines(graph, averageLoad, false, {255, 0, 0}, 2); + cv::rectangle(frame, cv::Rect{ + cv::Point{graphPos, yPos + textGraphSplittingLine}, + cv::Size{graphSize.width, graphSize.height - textGraphSplittingLine} + }, {0, 0, 0}); + strStream.str("CPU"); + if (!lastHistory.empty()) { + strStream << ": " << std::fixed << std::setprecision(1) + << std::accumulate(lastHistory.back().begin(), lastHistory.back().end(), 0.0) + / lastHistory.back().size() * 100 << '%'; + } + int baseline; + int textWidth = cv::getTextSize(strStream.str(), + cv::FONT_HERSHEY_SIMPLEX, + textGraphSplittingLine * 0.04, + 1, + &baseline).width; + cv::putText(graph, + strStream.str(), + cv::Point{(graphSize.width - textWidth) / 2, textGraphSplittingLine - 1}, + cv::FONT_HERSHEY_SIMPLEX, + textGraphSplittingLine * 0.04, + {70, 0, 0}, + 1); + graphPos += graphSize.width + graphPadding; + } + + if (distributionCpuEnabled && --numberOfEnabledMonitors >= 0) { + std::deque<std::vector<double>> lastHistory = cpuMonitor.getLastHistory(); + cv::Rect intersection = cv::Rect{cv::Point(graphPos, yPos), graphSize} & cv::Rect{0, 0, frame.cols, frame.rows}; + if (!intersection.area()) { + return; + } + cv::Mat graph = frame(intersection); + graph = graph / 2 + cv::Scalar{127, 127, 127}; + + if (!lastHistory.empty()) { + int rectXPos = 0; + int step = (graph.cols + lastHistory.back().size() - 1) / lastHistory.back().size(); // round up + double sum = 0; + for (double coreLoad : lastHistory.back()) { + sum += coreLoad; + int height = static_cast<int>(graphRectHeight * coreLoad); + cv::Rect pillar{cv::Point{rectXPos, graph.rows - height}, cv::Size{step, height}}; + cv::rectangle(graph, pillar, {255, 0, 0}, cv::FILLED); + cv::rectangle(graph, pillar, {0, 0, 0}); + rectXPos += step; + } + sum /= lastHistory.back().size(); + int yLine = graph.rows - static_cast<int>(graphRectHeight * sum); + cv::line(graph, cv::Point{0, yLine}, cv::Point{graph.cols, yLine}, {0, 255, 0}, 2); + } + cv::Rect border{cv::Point{graphPos, yPos + textGraphSplittingLine}, + cv::Size{graphSize.width, graphSize.height - textGraphSplittingLine}}; + cv::rectangle(frame, border, {0, 0, 0}); + strStream.str("Core load"); + if (!lastHistory.empty()) { + strStream << ": " << std::fixed << std::setprecision(1) + << std::accumulate(lastHistory.back().begin(), lastHistory.back().end(), 0.0) + / lastHistory.back().size() * 100 << '%'; + } + int baseline; + int textWidth = cv::getTextSize(strStream.str(), + cv::FONT_HERSHEY_SIMPLEX, + textGraphSplittingLine * 0.04, + 1, + &baseline).width; + cv::putText(graph, + strStream.str(), + cv::Point{(graphSize.width - textWidth) / 2, textGraphSplittingLine - 1}, + cv::FONT_HERSHEY_SIMPLEX, + textGraphSplittingLine * 0.04, + {0, 70, 0}); + graphPos += graphSize.width + graphPadding; + } + + if (memoryMonitor.getHistorySize() > 1 && possibleHistorySize > 1 && --numberOfEnabledMonitors >= 0) { + std::deque<std::pair<double, double>> lastHistory = memoryMonitor.getLastHistory(); + cv::Rect intersection = cv::Rect{cv::Point(graphPos, yPos), graphSize} & cv::Rect{0, 0, frame.cols, frame.rows}; + if (!intersection.area()) { + return; + } + cv::Mat graph = frame(intersection); + graph = graph / 2 + cv::Scalar{127, 127, 127}; + int histxPos = graph.cols - 1; + double range = std::min(memoryMonitor.getMaxMemTotal() + memoryMonitor.getMaxSwap(), + (memoryMonitor.getMaxMem() + memoryMonitor.getMaxSwap()) * 1.2); + if (lastHistory.size() > 1) { + for (auto memUsageIt = lastHistory.rbegin(); memUsageIt != lastHistory.rend() - 1; ++memUsageIt) { + constexpr double SWAP_THRESHOLD = 10.0 / 1024; // 10 MiB + cv::Vec3b color = + (memoryMonitor.getMemTotal() * 0.95 > memUsageIt->first) || (memUsageIt->second < SWAP_THRESHOLD) ? + cv::Vec3b{0, 255, 255} : + cv::Vec3b{0, 0, 255}; + cv::Point right{histxPos, + graph.rows - static_cast<int>(graphRectHeight * (memUsageIt->first + memUsageIt->second) / range)}; + cv::Point left{histxPos - sampleStep, + graph.rows - static_cast<int>( + graphRectHeight * ((memUsageIt + 1)->first + (memUsageIt + 1)->second) / range)}; + cv::line(graph, right, left, color, 2); + histxPos -= sampleStep; + } + } + + cv::Rect border{cv::Point{graphPos, yPos + textGraphSplittingLine}, + cv::Size{graphSize.width, graphSize.height - textGraphSplittingLine}}; + cv::rectangle(frame, {border}, {0, 0, 0}); + if (lastHistory.empty()) { + strStream.str("Memory"); + } else { + strStream.str(""); + strStream << std::fixed << std::setprecision(1) << lastHistory.back().first << " + " + << lastHistory.back().second << " GiB"; + } + int baseline; + int textWidth = cv::getTextSize(strStream.str(), + cv::FONT_HERSHEY_SIMPLEX, + textGraphSplittingLine * 0.04, + 1, + &baseline).width; + cv::putText(graph, + strStream.str(), + cv::Point{(graphSize.width - textWidth) / 2, textGraphSplittingLine - 1}, + cv::FONT_HERSHEY_SIMPLEX, + textGraphSplittingLine * 0.04, + {0, 35, 35}); + } +} + +std::vector<std::string> Presenter::reportMeans() const { + std::vector<std::string> collectedData; + if (cpuMonitor.getHistorySize() > 1 || distributionCpuEnabled || memoryMonitor.getHistorySize() > 1) { + collectedData.push_back("Resources usage:"); + } + if (cpuMonitor.getHistorySize() > 1) { + std::ostringstream collectedDataStream; + collectedDataStream << std::fixed << std::setprecision(1); + collectedDataStream << "\tMean core utilization: "; + for (double mean : cpuMonitor.getMeanCpuLoad()) { + collectedDataStream << mean * 100 << "% "; + } + collectedData.push_back(collectedDataStream.str()); + } + if (distributionCpuEnabled) { + std::ostringstream collectedDataStream; + collectedDataStream << std::fixed << std::setprecision(1); + std::vector<double> meanCpuLoad = cpuMonitor.getMeanCpuLoad(); + double mean = std::accumulate(meanCpuLoad.begin(), meanCpuLoad.end(), 0.0) / meanCpuLoad.size(); + collectedDataStream << "\tMean CPU utilization: " << mean * 100 << "%"; + collectedData.push_back(collectedDataStream.str()); + } + if (memoryMonitor.getHistorySize() > 1) { + std::ostringstream collectedDataStream; + collectedDataStream << std::fixed << std::setprecision(1); + collectedDataStream << "\tMemory mean usage: " << memoryMonitor.getMeanMem() << " GiB"; + collectedData.push_back(collectedDataStream.str()); + collectedDataStream.str(""); + collectedDataStream << "\tMean swap usage: " << memoryMonitor.getMeanSwap() << " GiB"; + collectedData.push_back(collectedDataStream.str()); + } + + return collectedData; +} diff --git a/python/openvino/runtime/common/monitors/src/query_wrapper.cpp b/python/openvino/runtime/common/monitors/src/query_wrapper.cpp new file mode 100644 index 0000000..5c238d1 --- /dev/null +++ b/python/openvino/runtime/common/monitors/src/query_wrapper.cpp @@ -0,0 +1,22 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "monitors/query_wrapper.h" + +#include <Windows.h> +#include <system_error> + +QueryWrapper::QueryWrapper() { + PDH_STATUS status = PdhOpenQuery(NULL, NULL, &query); + if (ERROR_SUCCESS != status) { + throw std::system_error(status, std::system_category(), "PdhOpenQuery() failed"); + } +} +QueryWrapper::~QueryWrapper() { + PdhCloseQuery(query); +} + +QueryWrapper::operator PDH_HQUERY() const { + return query; +} |
