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 #include <core/perf/intf_performance_trace.h>
39
40 CORE_BEGIN_NAMESPACE()
41 using BASE_NS::make_unique;
42 using BASE_NS::pair;
43 using BASE_NS::string_view;
44 using BASE_NS::Uid;
45 using BASE_NS::vector;
46
47 struct PerformanceDataManager::InternalData {
48 NameToPerformanceMap data;
49 };
50
51 namespace {
52 #if (CORE_PERF_ENABLED == 1)
UpdateTimingData(const string_view subCategory,const string_view name,const int64_t microSeconds,PerformanceDataManager::TypeDataSet & dataSet)53 void UpdateTimingData(const string_view subCategory, const string_view name, const int64_t microSeconds,
54 PerformanceDataManager::TypeDataSet& dataSet)
55 {
56 auto iter = dataSet.find(subCategory);
57 if (iter != dataSet.end()) {
58 auto dataIter = iter->second.data.find(name);
59 if (dataIter != iter->second.data.end()) {
60 auto& ref = dataIter->second;
61 const int64_t arrayIndex = ref.counter % IPerformanceDataManager::TIMING_DATA_POOL_SIZE;
62 ref.counter++;
63 ref.currentTime = microSeconds;
64 if (microSeconds < ref.minTime) {
65 ref.minTime = microSeconds;
66 }
67 if (microSeconds > ref.maxTime) {
68 ref.maxTime = microSeconds;
69 }
70 ref.timings[arrayIndex] = microSeconds;
71 int64_t frameAverage = 0;
72 for (const auto& timingsRef : ref.timings) {
73 frameAverage += timingsRef;
74 }
75 ref.averageTime = frameAverage / IPerformanceDataManager::TIMING_DATA_POOL_SIZE;
76 ref.totalTime += ref.currentTime;
77 if (ref.totalTime > ref.maxTotalTime) {
78 ref.maxTotalTime = ref.totalTime;
79 }
80 } else {
81 // new value
82 auto& ref = iter->second.data[name];
83 ref = {
84 microSeconds, // currentTime
85 microSeconds, // maxTime
86 microSeconds, // minTime
87 microSeconds, // averageTime
88 microSeconds, // totalTime
89 1, // counter
90 };
91 std::fill_n(ref.timings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
92 }
93 } else {
94 // new subcategory and new value
95 auto& ref = dataSet[subCategory].data[name];
96 ref = {
97 microSeconds, // currentTime
98 microSeconds, // maxTime
99 microSeconds, // minTime
100 microSeconds, // averageTime
101 microSeconds, // totalTime
102 1, // counter
103 };
104 std::fill_n(ref.timings, IPerformanceDataManager::TIMING_DATA_POOL_SIZE, microSeconds);
105 }
106 }
107
GetTimingData(const PerformanceDataManager::TypeDataSet & typeData)108 vector<IPerformanceDataManager::PerformanceData> GetTimingData(const PerformanceDataManager::TypeDataSet& typeData)
109 {
110 vector<IPerformanceDataManager::PerformanceData> data;
111 data.reserve(typeData.size());
112 for (const auto& typeRef : typeData) {
113 IPerformanceDataManager::PerformanceData& pd = data.emplace_back();
114 pd.subCategory = typeRef.first;
115 for (const auto& perfRef : typeRef.second.data) {
116 pd.timings[perfRef.first] = perfRef.second;
117 }
118 }
119 return data;
120 }
121 #endif // CORE_PERF_ENABLED
122 } // namespace
123
124 PerformanceDataManager::~PerformanceDataManager() = default;
125
PerformanceDataManager(const string_view category,PerformanceDataManagerFactory & factory)126 PerformanceDataManager::PerformanceDataManager(const string_view category, PerformanceDataManagerFactory& factory)
127 : category_(category), factory_(factory)
128 {}
129
GetCategory() const130 string_view PerformanceDataManager::GetCategory() const
131 {
132 return category_;
133 }
134
135 using Clock = std::chrono::steady_clock;
136
BeginTimer()137 IPerformanceDataManager::TimerHandle PerformanceDataManager::BeginTimer()
138 {
139 return static_cast<IPerformanceDataManager::TimerHandle>(Clock::now().time_since_epoch().count());
140 }
141
EndTimer(IPerformanceDataManager::TimerHandle handle)142 int64_t PerformanceDataManager::EndTimer(IPerformanceDataManager::TimerHandle handle)
143 {
144 using std::chrono::duration_cast;
145 using std::chrono::microseconds;
146 const auto dt = Clock::now().time_since_epoch() - Clock::duration(handle);
147 return static_cast<int64_t>(duration_cast<microseconds>(dt).count());
148 }
149
UpdateData(const string_view subCategory,const string_view name,const int64_t microSeconds)150 void PerformanceDataManager::UpdateData([[maybe_unused]] const string_view subCategory,
151 [[maybe_unused]] const string_view name, [[maybe_unused]] const int64_t microSeconds)
152 {
153 #if (CORE_PERF_ENABLED == 1)
154 std::lock_guard<std::mutex> lock(dataMutex_);
155 UpdateTimingData(subCategory, name, microSeconds, data_);
156 #endif
157 }
158
ResetData()159 void PerformanceDataManager::ResetData()
160 {
161 #if (CORE_PERF_ENABLED == 1)
162 std::lock_guard<std::mutex> lock(dataMutex_);
163 data_.clear();
164 #endif
165 }
166
GetData() const167 vector<IPerformanceDataManager::PerformanceData> PerformanceDataManager::GetData() const
168 {
169 #if (CORE_PERF_ENABLED == 1)
170 std::lock_guard<std::mutex> lock(dataMutex_);
171 return GetTimingData(data_);
172 #else
173 return {};
174 #endif
175 }
176
RemoveData(const string_view subCategory)177 void PerformanceDataManager::RemoveData([[maybe_unused]] const string_view subCategory)
178 {
179 #if (CORE_PERF_ENABLED == 1)
180 std::lock_guard<std::mutex> lock(dataMutex_);
181 data_.erase(subCategory);
182 #endif
183 }
184
DumpToLog() const185 void PerformanceDataManager::DumpToLog() const
186 {
187 #if (CORE_PERF_ENABLED == 1)
188 std::lock_guard<std::mutex> lock(dataMutex_);
189
190 constexpr const string_view formatLegend = "%8s %8s %8s %9s %8s (microseconds)";
191 constexpr const string_view formatData = "%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %9" PRIu64 " %8" PRIu64 " %s::%s";
192
193 CORE_LOG_I(formatLegend.data(), "avg", "min", "max", "total", "count");
194
195 for (const auto& typeRef : data_) {
196 for (const auto& perfRef : typeRef.second.data) {
197 const int64_t counter = BASE_NS::Math::max(perfRef.second.counter, int64_t(1));
198 CORE_LOG_I(formatData.data(), perfRef.second.totalTime / counter, perfRef.second.minTime,
199 perfRef.second.maxTime, perfRef.second.totalTime, perfRef.second.counter, typeRef.first.c_str(),
200 perfRef.first.c_str());
201 }
202 }
203 #endif
204 }
205
206 // IInterface
GetInterface(const Uid & uid) const207 const IInterface* PerformanceDataManager::GetInterface(const Uid& uid) const
208 {
209 if ((uid == IPerformanceDataManager::UID) || (uid == IInterface::UID)) {
210 return this;
211 }
212 return nullptr;
213 }
214
GetInterface(const Uid & uid)215 IInterface* PerformanceDataManager::GetInterface(const Uid& uid)
216 {
217 if ((uid == IPerformanceDataManager::UID) || (uid == IInterface::UID)) {
218 return this;
219 }
220 return nullptr;
221 }
222
Ref()223 void PerformanceDataManager::Ref() {}
224
Unref()225 void PerformanceDataManager::Unref() {}
226
PerformanceDataManagerFactory(IPluginRegister & registry)227 PerformanceDataManagerFactory::PerformanceDataManagerFactory(IPluginRegister& registry) {}
228
SetPerformanceTrace(const Uid & uid,IPerformanceTrace::Ptr && trace)229 void PerformanceDataManagerFactory::SetPerformanceTrace(
230 [[maybe_unused]] const Uid& uid,
231 [[maybe_unused]] IPerformanceTrace::Ptr&& trace)
232 {
233 #if (CORE_PERF_ENABLED == 1)
234 perfTraces_.push_back({ uid, BASE_NS::move(trace) });
235 #endif
236 }
237
RemovePerformanceTrace(const BASE_NS::Uid & uid)238 void PerformanceDataManagerFactory::RemovePerformanceTrace([[maybe_unused]] const BASE_NS::Uid& uid)
239 {
240 #if (CORE_PERF_ENABLED == 1)
241 perfTraces_.erase(std::remove_if(perfTraces_.begin(), perfTraces_.end(),
242 [uid](const RegisteredPerformanceTrace& info) { return info.uid == uid; }),
243 perfTraces_.cend());
244 #endif
245 }
246
GetFirstPerformanceTrace() const247 IPerformanceTrace* PerformanceDataManagerFactory::GetFirstPerformanceTrace() const
248 {
249 return perfTraces_.size() > 0 ? perfTraces_.at(0).instance.get() : nullptr;
250 }
251
252 PerformanceDataManagerFactory::~PerformanceDataManagerFactory() = default;
253
Get(const string_view category)254 IPerformanceDataManager* PerformanceDataManagerFactory::Get([[maybe_unused]] const string_view category)
255 {
256 #if (CORE_PERF_ENABLED == 1)
257 std::lock_guard lock(mutex_);
258 if (auto pos = managers_.find(category); pos != managers_.end()) {
259 return pos->second.get();
260 }
261 auto inserted = managers_.insert({ category, make_unique<PerformanceDataManager>(category, *this) });
262 return inserted.first->second.get();
263 #else
264 return {};
265 #endif
266 }
267
GetAllCategories() const268 vector<IPerformanceDataManager*> PerformanceDataManagerFactory::GetAllCategories() const
269 {
270 vector<IPerformanceDataManager*> categories;
271 #if (CORE_PERF_ENABLED == 1)
272 std::lock_guard lock(mutex_);
273 categories.reserve(managers_.size());
274 for (const auto& manager : managers_) {
275 if (auto* mgrPtr = manager.second.get(); mgrPtr) {
276 categories.push_back(mgrPtr);
277 }
278 }
279 #endif
280 return categories;
281 }
282
283 // IInterface
GetInterface(const Uid & uid) const284 const IInterface* PerformanceDataManagerFactory::GetInterface(const Uid& uid) const
285 {
286 if ((uid == IPerformanceDataManagerFactory::UID) || (uid == IInterface::UID)) {
287 return this;
288 }
289 return nullptr;
290 }
291
GetInterface(const Uid & uid)292 IInterface* PerformanceDataManagerFactory::GetInterface(const Uid& uid)
293 {
294 if ((uid == IPerformanceDataManagerFactory::UID) || (uid == IInterface::UID)) {
295 return this;
296 }
297 return nullptr;
298 }
299
Ref()300 void PerformanceDataManagerFactory::Ref() {}
301
Unref()302 void PerformanceDataManagerFactory::Unref() {}
303
GetLogger()304 ILogger::IOutput::Ptr PerformanceDataManagerFactory::GetLogger()
305 {
306 return ILogger::IOutput::Ptr { new PerformanceTraceLogger(this) };
307 }
308
309 BASE_NS::array_view<const PerformanceDataManagerFactory::RegisteredPerformanceTrace>
GetPerformanceTraces() const310 PerformanceDataManagerFactory::GetPerformanceTraces() const
311 {
312 return BASE_NS::array_view(perfTraces_.data(), perfTraces_.size());
313 }
314
Write(ILogger::LogLevel logLevel,BASE_NS::string_view filename,int lineNumber,BASE_NS::string_view message)315 void PerformanceTraceLogger::Write(
316 ILogger::LogLevel logLevel, BASE_NS::string_view filename, int lineNumber, BASE_NS::string_view message)
317 {
318 if (auto trace = factory_->GetFirstPerformanceTrace()) {
319 trace->Message(message.data(), message.size(), 0);
320 }
321 }
322 CORE_END_NAMESPACE()
323