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