* Copyright (c) 2022 Huawei Device Co., Ltd.
* 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 "performance_data_manager.h"
#include <algorithm>
#include <chrono>
#include <cinttypes>
#include <cstdint>
#include <map>
#include <mutex>
#include <base/containers/array_view.h>
#include <base/containers/fixed_string.h>
#include <base/containers/string.h>
#include <base/containers/string_view.h>
#include <base/containers/type_traits.h>
#include <base/containers/unique_ptr.h>
#include <base/containers/unordered_map.h>
#include <base/containers/vector.h>
#include <base/math/mathf.h>
#include <base/namespace.h>
#include <base/util/uid.h>
#include <core/log.h>
#include <core/namespace.h>
#include <core/perf/intf_performance_data_manager.h>
#include <core/perf/intf_performance_trace.h>
CORE_BEGIN_NAMESPACE()
using BASE_NS::array_view;
using BASE_NS::make_unique;
using BASE_NS::pair;
using BASE_NS::string;
using BASE_NS::string_view;
using BASE_NS::Uid;
using BASE_NS::vector;
struct PerformanceDataManager::InternalData {
NameToPerformanceMap data;
};
namespace {
#if (CORE_PERF_ENABLED == 1)
void UpdateTimingData(const string_view subCategory, const string_view name, const int64_t microSeconds,
const PerformanceDataManager::PerformanceTimingData::DataType type, PerformanceDataManager::TypeDataSet& dataSet)
{
auto iter = dataSet.find(subCategory);
if (iter != dataSet.end()) {
auto dataIter = iter->second.data.find(name);
if (dataIter != iter->second.data.end()) {
auto& ref = dataIter->second;
const int64_t arrayIndex = ref.counter % IPerformanceDataManager::TIMING_DATA_POOL_SIZE;
ref.counter++;
ref.currentTime = microSeconds;
if (microSeconds > ref.maxTime) {
ref.maxTime = microSeconds;
}
if (microSeconds < ref.minTime) {
ref.minTime = microSeconds;
}
ref.timings[arrayIndex] = microSeconds;
int64_t frameAverage = 0;
for (const auto& timingsRef : ref.timings) {
frameAverage += timingsRef;
}
ref.averageTime = frameAverage / IPerformanceDataManager::TIMING_DATA_POOL_SIZE;
ref.averageTimings[arrayIndex] = ref.averageTime;
ref.totalTime += ref.currentTime;
if (ref.totalTime > ref.maxTotalTime) {
ref.maxTotalTime = ref.totalTime;
}
ref.type = type;
} else {
auto& ref = iter->second.data[name];
ref = {
microSeconds,
microSeconds,
microSeconds,
microSeconds,
microSeconds,
1,
};
ref.type = type;
std::fill_n(ref.timings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
std::fill_n(ref.averageTimings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
}
} else {
auto& ref = dataSet[subCategory].data[name];
ref = {
microSeconds,
microSeconds,
microSeconds,
microSeconds,
microSeconds,
1,
};
ref.type = type;
std::fill_n(ref.timings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
std::fill_n(ref.averageTimings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
}
}
vector<IPerformanceDataManager::PerformanceData> GetTimingData(const PerformanceDataManager::TypeDataSet& typeData)
{
vector<IPerformanceDataManager::PerformanceData> data;
data.reserve(typeData.size());
for (const auto& typeRef : typeData) {
IPerformanceDataManager::PerformanceData& pd = data.emplace_back();
pd.subCategory = typeRef.first;
for (const auto& perfRef : typeRef.second.data) {
pd.timings[perfRef.first] = perfRef.second;
}
}
return data;
}
#endif
}
PerformanceDataManager::~PerformanceDataManager() = default;
PerformanceDataManager::PerformanceDataManager(const string_view category, PerformanceDataManagerFactory& )
: category_(category)
{}
string_view PerformanceDataManager::GetCategory() const
{
return category_;
}
using Clock = std::chrono::steady_clock;
IPerformanceDataManager::TimerHandle PerformanceDataManager::BeginTimer()
{
return static_cast<IPerformanceDataManager::TimerHandle>(Clock::now().time_since_epoch().count());
}
int64_t PerformanceDataManager::EndTimer(IPerformanceDataManager::TimerHandle handle)
{
using std::chrono::duration_cast;
using std::chrono::microseconds;
const auto dt = Clock::now().time_since_epoch() - Clock::duration(handle);
return static_cast<int64_t>(duration_cast<microseconds>(dt).count());
}
void PerformanceDataManager::UpdateData([[maybe_unused]] const string_view subCategory,
[[maybe_unused]] const string_view name, [[maybe_unused]] const int64_t microSeconds)
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard<std::mutex> lock(dataMutex_);
UpdateTimingData(subCategory, name, microSeconds, PerformanceTimingData::DataType::MICROSECONDS, data_);
#endif
}
void PerformanceDataManager::UpdateData([[maybe_unused]] const string_view subCategory,
[[maybe_unused]] const string_view name, [[maybe_unused]] const int64_t value,
[[maybe_unused]] PerformanceTimingData::DataType type)
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard<std::mutex> lock(dataMutex_);
UpdateTimingData(subCategory, name, value, type, data_);
#endif
}
void PerformanceDataManager::ResetData()
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard<std::mutex> lock(dataMutex_);
data_.clear();
#endif
}
vector<IPerformanceDataManager::PerformanceData> PerformanceDataManager::GetData() const
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard<std::mutex> lock(dataMutex_);
return GetTimingData(data_);
#else
return {};
#endif
}
void PerformanceDataManager::RemoveData([[maybe_unused]] const string_view subCategory)
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard<std::mutex> lock(dataMutex_);
data_.erase(subCategory);
#endif
}
void PerformanceDataManager::GetSelectedCounters(CounterPairView data) const
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard<std::mutex> lock(dataMutex_);
const auto dataRef = GetTimingData(data_);
for (const auto& dRef : dataRef) {
for (const auto& tRef : dRef.timings) {
for (auto& inputDataRef : data) {
if (tRef.first == inputDataRef.first) {
inputDataRef.second += tRef.second.currentTime;
}
}
}
}
#else
#endif
}
IPerformanceDataManager::ComparisonData PerformanceDataManager::CompareCounters(
const ConstCounterPairView lhs, const ConstCounterPairView rhs) const
{
ComparisonData cd;
#if (CORE_PERF_ENABLED == 1)
if (lhs.size() != rhs.size()) {
cd.flags |= 1U;
} else {
for (size_t idx = 0; idx < lhs.size(); ++idx) {
const auto& currLhs = lhs[idx];
const auto& currRhs = rhs[idx];
if (currLhs.first != currRhs.first) {
cd.flags |= 1U;
} else if (currLhs.second != currRhs.second) {
cd.flags |= 1U;
cd.errorCounters.push_back(pair{currLhs.first, currLhs.second - currRhs.second});
}
}
}
#endif
return cd;
}
void PerformanceDataManager::DumpToLog() const
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard<std::mutex> lock(dataMutex_);
constexpr const string_view formatLegend = "%8s %8s %8s %9s %8s (microseconds)";
constexpr const string_view formatData = "%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %9" PRIu64 " %8" PRIu64 " %s::%s";
CORE_LOG_I(formatLegend.data(), "avg", "min", "max", "total", "count");
for (const auto& typeRef : data_) {
for (const auto& perfRef : typeRef.second.data) {
const int64_t counter = BASE_NS::Math::max(perfRef.second.counter, int64_t(1));
CORE_LOG_I(formatData.data(),
perfRef.second.totalTime / counter,
perfRef.second.minTime,
perfRef.second.maxTime,
perfRef.second.totalTime,
perfRef.second.counter,
typeRef.first.c_str(),
perfRef.first.c_str());
}
}
#endif
}
const IInterface* PerformanceDataManager::GetInterface(const Uid& uid) const
{
if ((uid == IPerformanceDataManager::UID) || (uid == IInterface::UID)) {
return this;
}
return nullptr;
}
IInterface* PerformanceDataManager::GetInterface(const Uid& uid)
{
if ((uid == IPerformanceDataManager::UID) || (uid == IInterface::UID)) {
return this;
}
return nullptr;
}
void PerformanceDataManager::Ref()
{}
void PerformanceDataManager::Unref()
{}
PerformanceDataManagerFactory::PerformanceDataManagerFactory(IPluginRegister& registry)
{}
void PerformanceDataManagerFactory::SetPerformanceTrace(
[[maybe_unused]] const Uid& uid, [[maybe_unused]] IPerformanceTrace::Ptr&& trace)
{
#if (CORE_PERF_ENABLED == 1)
perfTraces_.push_back({uid, BASE_NS::move(trace)});
#endif
}
void PerformanceDataManagerFactory::RemovePerformanceTrace([[maybe_unused]] const BASE_NS::Uid& uid)
{
#if (CORE_PERF_ENABLED == 1)
perfTraces_.erase(std::remove_if(perfTraces_.begin(),
perfTraces_.end(),
[uid](const RegisteredPerformanceTrace& info) { return info.uid == uid; }),
perfTraces_.cend());
#endif
}
IPerformanceTrace* PerformanceDataManagerFactory::GetFirstPerformanceTrace() const
{
return perfTraces_.size() > 0 ? perfTraces_.at(0).instance.get() : nullptr;
}
PerformanceDataManagerFactory::~PerformanceDataManagerFactory() = default;
IPerformanceDataManager* PerformanceDataManagerFactory::Get([[maybe_unused]] const string_view category)
{
#if (CORE_PERF_ENABLED == 1)
std::lock_guard lock(mutex_);
if (auto pos = managers_.find(category); pos != managers_.end()) {
return pos->second.get();
}
auto inserted = managers_.insert({category, make_unique<PerformanceDataManager>(category, *this)});
return inserted.first->second.get();
#else
return {};
#endif
}
vector<IPerformanceDataManager*> PerformanceDataManagerFactory::GetAllCategories() const
{
vector<IPerformanceDataManager*> categories;
#if (CORE_PERF_ENABLED == 1)
std::lock_guard lock(mutex_);
categories.reserve(managers_.size());
for (const auto& manager : managers_) {
if (auto* mgrPtr = manager.second.get(); mgrPtr) {
categories.push_back(mgrPtr);
}
}
#endif
return categories;
}
const IInterface* PerformanceDataManagerFactory::GetInterface(const Uid& uid) const
{
if ((uid == IPerformanceDataManagerFactory::UID) || (uid == IInterface::UID)) {
return this;
}
return nullptr;
}
IInterface* PerformanceDataManagerFactory::GetInterface(const Uid& uid)
{
if ((uid == IPerformanceDataManagerFactory::UID) || (uid == IInterface::UID)) {
return this;
}
return nullptr;
}
void PerformanceDataManagerFactory::Ref()
{}
void PerformanceDataManagerFactory::Unref()
{}
ILogger::IOutput::Ptr PerformanceDataManagerFactory::GetLogger()
{
return ILogger::IOutput::Ptr{new PerformanceTraceLogger(this)};
}
BASE_NS::array_view<const PerformanceDataManagerFactory::RegisteredPerformanceTrace>
PerformanceDataManagerFactory::GetPerformanceTraces() const
{
return BASE_NS::array_view(perfTraces_.data(), perfTraces_.size());
}
void PerformanceTraceLogger::Write(
ILogger::LogLevel logLevel, BASE_NS::string_view filename, int lineNumber, BASE_NS::string_view message)
{
if (auto trace = factory_->GetFirstPerformanceTrace()) {
trace->Message(message.data(), message.size(), 0);
}
}
CORE_END_NAMESPACE()