• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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