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 "timers.h"
17 #include "os/file.h"
18
19 #include <algorithm>
20 #include <cstring>
21
22 namespace panda {
23 TimeStartFunc Timer::timerStart = Timer::TimerStartDoNothing;
24 TimeEndFunc Timer::timerEnd = Timer::TimerEndDoNothing;
25 std::unordered_map<std::string_view, TimeRecord> Timer::timers_;
26 std::vector<std::string_view> Timer::events_;
27 std::mutex Timer::mutex_;
28 std::string Timer::perfFile_;
29
InitializeTimer(std::string & perfFile)30 void Timer::InitializeTimer(std::string &perfFile)
31 {
32 if (!perfFile.empty()) {
33 Timer::timerStart = Timer::TimerStartImpl;
34 Timer::timerEnd = Timer::TimerEndImpl;
35 perfFile_ = perfFile;
36 }
37 }
38
WriteFile(std::stringstream & ss,std::string & perfFile)39 void WriteFile(std::stringstream &ss, std::string &perfFile)
40 {
41 std::ofstream fs;
42 fs.open(panda::os::file::File::GetExtendedFilePath(perfFile));
43 if (!fs.is_open()) {
44 std::cerr << "Failed to open perf file: " << perfFile << ". Errro: " << std::strerror(errno) << std::endl;
45 return;
46 }
47 fs << ss.str();
48 fs.close();
49 }
50
DescentComparator(const std::pair<std::string,double> p1,const std::pair<std::string,double> p2)51 bool DescentComparator(const std::pair<std::string, double> p1, const std::pair<std::string, double> p2)
52 {
53 return p1.second > p2.second;
54 }
55
ProcessTimePointRecord(const std::string & file,const TimePointRecord & timePointRecord,double & eventTime,std::vector<std::pair<std::string,double>> & eachFileTime,std::vector<std::string> & incompleteRecords)56 void ProcessTimePointRecord(
57 const std::string& file,
58 const TimePointRecord& timePointRecord,
59 double& eventTime,
60 std::vector<std::pair<std::string, double>>& eachFileTime,
61 std::vector<std::string>& incompleteRecords)
62 {
63 if (timePointRecord.startTime != std::chrono::steady_clock::time_point() &&
64 timePointRecord.endTime != std::chrono::steady_clock::time_point()) {
65 auto duration = timePointRecord.endTime - timePointRecord.startTime;
66 if (duration.count() >= 0) {
67 auto t = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
68 eventTime += t;
69 if (!file.empty()) {
70 eachFileTime.emplace_back(file, t);
71 }
72 } else {
73 incompleteRecords.push_back(file + ": The end time is earlier than the start time");
74 }
75 } else {
76 incompleteRecords.push_back(file + ": " +
77 (timePointRecord.startTime == std::chrono::steady_clock::time_point() ?
78 "Lack of start time" : "Lack of end time"));
79 }
80 }
81
PrintTimers()82 void Timer::PrintTimers()
83 {
84 std::stringstream ss;
85 ss << "------------- Compilation time consumption in milliseconds: -------------" << std::endl;
86 ss << "Note: When compiling multiple files in parallel, " <<
87 "we will track the time consumption of each file individually. The output will aggregate these times, " <<
88 "potentially resulting in a total time consumption less than the sum of individual file times." <<
89 std::endl << std::endl;
90
91 std::vector<std::string> summedUpTimeString;
92 ss << "------------- Compilation time consumption of each file: ----------------" << std::endl;
93
94 for (auto &event: events_) {
95 auto &timeRecord = timers_.at(event);
96 auto formattedEvent =
97 std::string(timeRecord.level, ' ') + std::string(timeRecord.level, '#') + std::string(event);
98
99 double eventTime = 0.0;
100 std::vector<std::pair<std::string, double>> eachFileTime;
101 std::vector<std::string> incompleteRecords;
102
103 for (const auto &[file, timePointRecord] : timeRecord.timePoints) {
104 ProcessTimePointRecord(file, timePointRecord, eventTime, eachFileTime, incompleteRecords);
105 }
106
107 // print each file time consumption in descending order
108 if (!eachFileTime.empty()) {
109 std::sort(eachFileTime.begin(), eachFileTime.end(), DescentComparator);
110 ss << formattedEvent << ", time consumption of each file:" << std::endl;
111 }
112 for (auto &pair : eachFileTime) {
113 if (!pair.first.empty()) {
114 ss << pair.first << ": " << pair.second << " ms" <<std::endl;
115 }
116 }
117
118 // Print incomplete records
119 if (!incompleteRecords.empty()) {
120 ss << formattedEvent << ", Incomplete records:" << std::endl;
121 for (const auto& record : incompleteRecords) {
122 ss << " " << record << std::endl;
123 }
124 }
125
126 // collect the sum of each file's time consumption
127 std::stringstream eventSummedUpTimeStream;
128 eventSummedUpTimeStream << formattedEvent << ": " << eventTime << " ms";
129 summedUpTimeString.push_back(eventSummedUpTimeStream.str());
130 }
131 ss << "------------- Compilation time consumption summed up: -------------------" << std::endl;
132 for (auto &str: summedUpTimeString) {
133 ss << str << std::endl;
134 }
135 ss << "-------------------------------------------------------------------------" << std::endl;
136 WriteFile(ss, perfFile_);
137 }
138
TimerStartImpl(const std::string_view event,std::string fileName)139 void Timer::TimerStartImpl(const std::string_view event, std::string fileName)
140 {
141 TimePointRecord tpr;
142 tpr.startTime = std::chrono::steady_clock::now();
143 int level = 0;
144 auto eventIter = eventMap.find(event);
145 if (eventIter != eventMap.end()) {
146 level = eventIter->second;
147 } else {
148 std::cerr << "Undefined event: " << event << ". Please check!" << std::endl;
149 }
150
151 std::unique_lock<std::mutex> lock(mutex_);
152 auto iter = timers_.find(event);
153 if (iter != timers_.end()) {
154 iter->second.timePoints.emplace(fileName, tpr);
155 } else {
156 TimeRecord tr;
157 tr.timePoints.emplace(fileName, tpr);
158 tr.event = event;
159 tr.level = level;
160
161 timers_.emplace(event, tr);
162 events_.push_back(event);
163 }
164 }
165
TimerEndImpl(const std::string_view event,std::string fileName)166 void Timer::TimerEndImpl(const std::string_view event, std::string fileName)
167 {
168 auto endTime = std::chrono::steady_clock::now();
169 std::unique_lock<std::mutex> lock(mutex_);
170 auto time_record_iter = timers_.find(event);
171 if (time_record_iter == timers_.end()) {
172 std::cerr << "Event " << event << " not found in records, skip record end time!" << std::endl;
173 return;
174 }
175 auto& timePoints = time_record_iter->second.timePoints;
176 auto time_point_iter = timePoints.find(fileName);
177 if (time_point_iter == timePoints.end()) {
178 std::cerr << "Event " << event << " and file " << fileName <<
179 " start timer not found in records, skip record end time!" << std::endl;
180 return;
181 }
182 time_point_iter->second.endTime = endTime;
183 }
184
185 } // namespace panda
186