1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "performance_data_manager.h"
17 
18 #include <algorithm>
19 #include <chrono>
20 #include <cinttypes>
21 #include <cstdint>
22 #include <map>
23 #include <mutex>
24 
25 #include <base/containers/fixed_string.h>
26 #include <base/containers/string.h>
27 #include <base/containers/string_view.h>
28 #include <base/containers/type_traits.h>
29 #include <base/containers/unique_ptr.h>
30 #include <base/containers/unordered_map.h>
31 #include <base/containers/vector.h>
32 #include <base/math/mathf.h>
33 #include <base/namespace.h>
34 #include <base/util/uid.h>
35 #include <core/log.h>
36 #include <core/namespace.h>
37 #include <core/perf/intf_performance_data_manager.h>
38 
39 CORE_BEGIN_NAMESPACE()
40 using BASE_NS::make_unique;
41 using BASE_NS::pair;
42 using BASE_NS::string_view;
43 using BASE_NS::Uid;
44 using BASE_NS::vector;
45 
46 struct PerformanceDataManager::InternalData {
47     NameToPerformanceMap data;
48 };
49 
50 namespace {
51 #if (CORE_PERF_ENABLED == 1)
UpdateTimingData(const string_view subCategory,const string_view name,const int64_t microSeconds,PerformanceDataManager::TypeDataSet & dataSet)52 void UpdateTimingData(const string_view subCategory, const string_view name, const int64_t microSeconds,
53     PerformanceDataManager::TypeDataSet& dataSet)
54 {
55     auto iter = dataSet.find(subCategory);
56     if (iter != dataSet.end()) {
57         auto dataIter = iter->second.data.find(name);
58         if (dataIter != iter->second.data.end()) {
59             auto& ref = dataIter->second;
60             const int64_t arrayIndex = ref.counter % IPerformanceDataManager::TIMING_DATA_POOL_SIZE;
61             ref.counter++;
62             ref.currentTime = microSeconds;
63             if (microSeconds < ref.minTime) {
64                 ref.minTime = microSeconds;
65             }
66             if (microSeconds > ref.maxTime) {
67                 ref.maxTime = microSeconds;
68             }
69             ref.timings[arrayIndex] = microSeconds;
70             int64_t frameAverage = 0;
71             for (const auto& timingsRef : ref.timings) {
72                 frameAverage += timingsRef;
73             }
74             ref.averageTime = frameAverage / IPerformanceDataManager::TIMING_DATA_POOL_SIZE;
75             ref.totalTime += ref.currentTime;
76             if (ref.totalTime > ref.maxTotalTime) {
77                 ref.maxTotalTime = ref.totalTime;
78             }
79         } else {
80             // new value
81             auto& ref = iter->second.data[name];
82             ref = {
83                 microSeconds, // currentTime
84                 microSeconds, // maxTime
85                 microSeconds, // minTime
86                 microSeconds, // averageTime
87                 microSeconds, // totalTime
88                 1,            // counter
89             };
90             std::fill_n(ref.timings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
91         }
92     } else {
93         // new subcategory and new value
94         auto& ref = dataSet[subCategory].data[name];
95         ref = {
96             microSeconds, // currentTime
97             microSeconds, // maxTime
98             microSeconds, // minTime
99             microSeconds, // averageTime
100             microSeconds, // totalTime
101             1,            // counter
102         };
103         std::fill_n(ref.timings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
104     }
105 }
106 
GetTimingData(const PerformanceDataManager::TypeDataSet & typeData)107 vector<IPerformanceDataManager::PerformanceData> GetTimingData(const PerformanceDataManager::TypeDataSet& typeData)
108 {
109     vector<IPerformanceDataManager::PerformanceData> data;
110     data.reserve(typeData.size());
111     for (const auto& typeRef : typeData) {
112         IPerformanceDataManager::PerformanceData& pd = data.emplace_back();
113         pd.subCategory = typeRef.first;
114         for (const auto& perfRef : typeRef.second.data) {
115             pd.timings[perfRef.first] = perfRef.second;
116         }
117     }
118     return data;
119 }
120 #endif // CORE_PERF_ENABLED
121 } // namespace
122 
123 PerformanceDataManager::~PerformanceDataManager() = default;
124 
PerformanceDataManager(const string_view category)125 PerformanceDataManager::PerformanceDataManager(const string_view category) : category_(category) {}
126 
GetCategory() const127 string_view PerformanceDataManager::GetCategory() const
128 {
129     return category_;
130 }
131 
132 using Clock = std::chrono::system_clock;
133 
BeginTimer()134 IPerformanceDataManager::TimerHandle PerformanceDataManager::BeginTimer()
135 {
136     return static_cast<IPerformanceDataManager::TimerHandle>(Clock::now().time_since_epoch().count());
137 }
138 
EndTimer(IPerformanceDataManager::TimerHandle handle)139 int64_t PerformanceDataManager::EndTimer(IPerformanceDataManager::TimerHandle handle)
140 {
141     using std::chrono::duration_cast;
142     using std::chrono::microseconds;
143     const auto dt = Clock::now().time_since_epoch() - Clock::duration(handle);
144     return static_cast<int64_t>(duration_cast<microseconds>(dt).count());
145 }
146 
147 void PerformanceDataManager::UpdateData([[maybe_unused]] const string_view subCategory,
148     [[maybe_unused]] const string_view name, [[maybe_unused]] const int64_t microSeconds)
149 {
150 #if (CORE_PERF_ENABLED == 1)
151     std::lock_guard<std::mutex> lock(dataMutex_);
152     UpdateTimingData(subCategory, name, microSeconds, data_);
153 #endif
154 }
155 
ResetData()156 void PerformanceDataManager::ResetData()
157 {
158 #if (CORE_PERF_ENABLED == 1)
159     std::lock_guard<std::mutex> lock(dataMutex_);
160     data_.clear();
161 #endif
162 }
163 
GetData() const164 vector<IPerformanceDataManager::PerformanceData> PerformanceDataManager::GetData() const
165 {
166 #if (CORE_PERF_ENABLED == 1)
167     std::lock_guard<std::mutex> lock(dataMutex_);
168     return GetTimingData(data_);
169 #else
170     return {};
171 #endif
172 }
173 
174 void PerformanceDataManager::RemoveData([[maybe_unused]] const string_view subCategory)
175 {
176 #if (CORE_PERF_ENABLED == 1)
177     std::lock_guard<std::mutex> lock(dataMutex_);
178     data_.erase(subCategory);
179 #endif
180 }
181 
DumpToLog() const182 void PerformanceDataManager::DumpToLog() const
183 {
184 #if (CORE_PERF_ENABLED == 1)
185     std::lock_guard<std::mutex> lock(dataMutex_);
186 
187     constexpr const string_view formatLegend = "%8s %8s %8s %9s %8s (microseconds)";
188     constexpr const string_view formatData = "%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %9" PRIu64 " %8" PRIu64 " %s::%s";
189 
190     CORE_LOG_I(formatLegend.data(), "avg", "min", "max", "total", "count");
191 
192     for (const auto& typeRef : data_) {
193         for (const auto& perfRef : typeRef.second.data) {
194             const int64_t counter = BASE_NS::Math::max(perfRef.second.counter, int64_t(1));
195             CORE_LOG_I(formatData.data(), perfRef.second.totalTime / counter, perfRef.second.minTime,
196                 perfRef.second.maxTime, perfRef.second.totalTime, perfRef.second.counter, typeRef.first.c_str(),
197                 perfRef.first.c_str());
198         }
199     }
200 #endif
201 }
202 
203 // IInterface
GetInterface(const Uid & uid) const204 const IInterface* PerformanceDataManager::GetInterface(const Uid& uid) const
205 {
206     if ((uid == IPerformanceDataManager::UID) || (uid == IInterface::UID)) {
207         return this;
208     }
209     return nullptr;
210 }
211 
GetInterface(const Uid & uid)212 IInterface* PerformanceDataManager::GetInterface(const Uid& uid)
213 {
214     if ((uid == IPerformanceDataManager::UID) || (uid == IInterface::UID)) {
215         return this;
216     }
217     return nullptr;
218 }
219 
Ref()220 void PerformanceDataManager::Ref() {}
221 
Unref()222 void PerformanceDataManager::Unref() {}
223 
224 IPerformanceDataManager* PerformanceDataManagerFactory::Get([[maybe_unused]] const string_view category)
225 {
226 #if (CORE_PERF_ENABLED == 1)
227     std::lock_guard lock(mutex_);
228     if (auto pos = managers_.find(category); pos != managers_.end()) {
229         return pos->second.get();
230     }
231     auto inserted = managers_.insert({ category, make_unique<PerformanceDataManager>(category) });
232     return inserted.first->second.get();
233 #else
234     return {};
235 #endif
236 }
237 
GetAllCategories() const238 vector<IPerformanceDataManager*> PerformanceDataManagerFactory::GetAllCategories() const
239 {
240     vector<IPerformanceDataManager*> categories;
241 #if (CORE_PERF_ENABLED == 1)
242     std::lock_guard lock(mutex_);
243     categories.reserve(managers_.size());
244     for (const auto& manager : managers_) {
245         if (auto* mgrPtr = manager.second.get(); mgrPtr) {
246             categories.push_back(mgrPtr);
247         }
248     }
249 #endif
250     return categories;
251 }
252 
253 // IInterface
GetInterface(const Uid & uid) const254 const IInterface* PerformanceDataManagerFactory::GetInterface(const Uid& uid) const
255 {
256     if ((uid == IPerformanceDataManagerFactory::UID) || (uid == IInterface::UID)) {
257         return this;
258     }
259     return nullptr;
260 }
261 
GetInterface(const Uid & uid)262 IInterface* PerformanceDataManagerFactory::GetInterface(const Uid& uid)
263 {
264     if ((uid == IPerformanceDataManagerFactory::UID) || (uid == IInterface::UID)) {
265         return this;
266     }
267     return nullptr;
268 }
269 
Ref()270 void PerformanceDataManagerFactory::Ref() {}
271 
Unref()272 void PerformanceDataManagerFactory::Unref() {}
273 CORE_END_NAMESPACE()
274