* Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
*
* 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.
*/
* Description: Perf manager.
*/
#include "datasystem/common/perf/perf_manager.h"
#include <sstream>
#include <nlohmann/json.hpp>
#include "datasystem/common/log/log.h"
namespace datasystem {
#ifdef ENABLE_PERF
const uint64_t SECONDS_TO_NANO_UNIT = 1000ul * 1000ul * 1000ul;
const uint64_t TRIGGER_PERF_LOG_NANO_INTERVAL = 60ul * SECONDS_TO_NANO_UNIT;
* PerfInfo to json value
* @param[out] json value
* @param[in] info PerfInfo
*/
static void to_json(nlohmann::json &json, const PerfInfo &info)
{
uint64_t count = info.count.load();
uint64_t totalTime = info.totalTime.load();
uint64_t avgTime = 0;
if (count > 0) {
avgTime = totalTime / count;
}
json = {
{ "count", count },
{ "minTime", info.minTime.load() },
{ "maxTime", info.maxTime.load() },
{ "totalTime", totalTime },
{ "avgTime", avgTime },
{ "maxFrequency", info.maxFrequency.load() },
};
}
PerfManager *PerfManager::Instance()
{
static PerfManager inst;
return &inst;
}
PerfManager::PerfManager()
{
#define PERF_KEY_DEF(keyEnum) #keyEnum,
static const char *keyNames[] = {
"NONE",
#include "datasystem/common/perf/perf_point.def"
};
#undef PERF_KEY_DEF
constexpr size_t enumCount = sizeof(keyNames) / sizeof(char *);
static PerfInfo perfInfoList[enumCount];
prevTickTime_ = clock::now();
prevLogTime_ = clock::now();
perfKeyCount_ = enumCount;
perfInfoList_ = perfInfoList;
keyNameList_ = keyNames;
}
void PerfManager::Add(PerfKey key, uint64_t elapsed)
{
auto index = static_cast<size_t>(key);
if (index >= perfKeyCount_) {
return;
}
auto &info = perfInfoList_[index];
info.count.fetch_add(1, std::memory_order_relaxed);
info.totalTime.fetch_add(elapsed, std::memory_order_relaxed);
info.tickCount.fetch_add(1, std::memory_order_relaxed);
uint64_t preValue = info.maxTime.load();
while (elapsed > preValue && !info.maxTime.compare_exchange_weak(preValue, elapsed)) {
}
preValue = info.minTime.load();
while (elapsed < preValue && !info.minTime.compare_exchange_weak(preValue, elapsed)) {
}
}
void PerfManager::ResetPerfLog()
{
for (size_t i = 0; i < perfKeyCount_; i++) {
perfInfoList_[i].Reset();
}
LOG(INFO) << "Reset PerfLog in perf manager......";
}
std::string PerfManager::GetPerfLog() const
{
std::stringstream ss;
std::string prefix;
for (size_t i = 0; i < perfKeyCount_; i++) {
const PerfInfo &info = perfInfoList_[i];
auto keyName = keyNameList_[i];
uint64_t count = info.count.load();
if (count > 0) {
ss << prefix << keyName << ": " << nlohmann::json(info);
if (prefix.empty()) {
prefix = '\n';
}
}
}
return ss.str();
}
void PerfManager::GetPerfInfoList(std::vector<std::pair<std::string, PerfInfo>> &perfInfoList) const
{
for (size_t i = 0; i < perfKeyCount_; i++) {
const PerfInfo &info = perfInfoList_[i];
auto keyName = keyNameList_[i];
uint64_t count = info.count.load();
if (count > 0) {
perfInfoList.emplace_back(keyName, info);
}
}
}
void PerfManager::PrintPerfLog() const
{
std::string perfLog = PerfManager::Instance()->GetPerfLog();
LOG(INFO) << "[Perf Log]:\n" << perfLog;
}
void PerfManager::Tick()
{
std::chrono::time_point<clock> nowTime = clock::now();
uint64_t elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(nowTime - prevLogTime_).count();
uint64_t tickElapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(nowTime - prevTickTime_).count();
if (tickElapsed > 0) {
prevTickTime_ = nowTime;
for (size_t i = 0; i < perfKeyCount_; i++) {
PerfInfo &info = perfInfoList_[i];
uint64_t tickCount = info.tickCount.load();
uint64_t maxFrequency = info.maxFrequency.load();
uint64_t frequency = tickCount * SECONDS_TO_NANO_UNIT / tickElapsed;
if (frequency > maxFrequency) {
info.maxFrequency.store(frequency);
}
info.tickCount.store(0);
}
}
if (elapsed >= TRIGGER_PERF_LOG_NANO_INTERVAL) {
prevLogTime_ = nowTime;
std::string perfLog = GetPerfLog();
LOG(INFO) << "[Perf Log]:\n" << perfLog;
}
}
PerfPoint::~PerfPoint() noexcept
{
if (!isRecord_) {
Record();
}
}
void PerfPoint::Record()
{
uint64_t elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(clock::now() - beg_).count();
PerfManager *perfManager = PerfManager::Instance();
if (perfManager != nullptr) {
perfManager->Add(key_, elapsed);
}
isRecord_ = true;
}
void PerfPoint::Reset(PerfKey key)
{
beg_ = clock::now();
isRecord_ = false;
if (key != PerfKey::NONE) {
key_ = key;
}
}
void PerfPoint::RecordAndReset(PerfKey key)
{
Record();
Reset(key);
}
void PerfPoint::RecordElapsed(PerfKey key, uint64_t elapsed)
{
PerfManager *perfManager = PerfManager::Instance();
if (perfManager != nullptr) {
perfManager->Add(key, elapsed);
}
}
#else
PerfManager *PerfManager::Instance()
{
return nullptr;
}
PerfManager::PerfManager()
{
}
void PerfManager::Add(PerfKey key, uint64_t elapsed)
{
(void)key;
(void)elapsed;
}
void PerfManager::ResetPerfLog()
{
}
std::string PerfManager::GetPerfLog() const
{
return "";
}
void PerfManager::GetPerfInfoList(std::vector<std::pair<std::string, PerfInfo>> &perfInfoList) const
{
(void)perfInfoList;
}
void PerfManager::PrintPerfLog() const
{
}
void PerfManager::Tick()
{
}
PerfPoint::~PerfPoint() noexcept
{
}
void PerfPoint::Record()
{
}
void PerfPoint::Reset(PerfKey key)
{
(void)key;
}
void PerfPoint::RecordAndReset(PerfKey key)
{
(void)key;
}
void PerfPoint::RecordElapsed(PerfKey key, uint64_t elapsed)
{
(void)key;
(void)elapsed;
}
#endif
}