Line data Source code
1 : /////////////////////////////////////////////////////////////////////////// 2 : // Spetabaru - Berenger Bramas MPCDF - 2017 3 : // Under LGPL Licence, please you must read the LICENCE file. 4 : /////////////////////////////////////////////////////////////////////////// 5 : #ifndef SPSVGTRACE_HPP 6 : #define SPSVGTRACE_HPP 7 : 8 : #include <fstream> 9 : #include <cmath> 10 : #include <iterator> 11 : 12 : #include "Task/SpAbstractTask.hpp" 13 : #include "Utils/SpTimePoint.hpp" 14 : #include "Utils/small_vector.hpp" 15 : #include "Random/SpMTGenerator.hpp" 16 : 17 : 18 : namespace SpSvgTrace { 19 : 20 14 : inline void GenerateTrace(const std::string& outputFilename, const std::list<SpAbstractTask*>& tasksFinished, 21 : const SpTimePoint& startingTime, const bool showDependences) { 22 : 23 28 : std::ofstream svgfile(outputFilename); 24 : 25 14 : if(svgfile.is_open() == false){ 26 0 : throw std::invalid_argument("Cannot open filename : " + outputFilename); 27 : } 28 : 29 : const auto threadIds = 30 14 : [&]() { 31 14 : std::vector<long int> res; 32 14 : res.reserve(tasksFinished.size()); 33 : std::transform(std::begin(tasksFinished), std::end(tasksFinished), std::back_inserter(res), 34 599 : [](SpAbstractTask* task){ 35 599 : return task->getThreadIdComputer(); 36 14 : }); 37 : 38 14 : std::sort(std::begin(res), std::end(res)); 39 14 : res.erase(std::unique(std::begin(res), std::end(res)), std::end(res)); 40 14 : return res; 41 28 : }(); 42 : 43 14 : const int nbThreads = static_cast<int>(threadIds.size()); 44 : 45 : const auto threadIdsToVerticalSlotPosMap = 46 14 : [&]() { 47 14 : std::unordered_map<long int, long int> mapping; 48 42 : for(long int i=0; i < static_cast<long int>(threadIds.size()); i++) { 49 28 : mapping[threadIds[i]] = i+1; 50 : } 51 14 : return mapping; 52 28 : }(); 53 : 54 14 : const long int vsizeperthread = std::max(100, std::min(200, 2000/nbThreads)); 55 14 : const long int vmargin = 100; 56 14 : const long int threadstrock = 5; 57 14 : const long int vmarginthreadstats = 10; 58 14 : const long int vdim = nbThreads * (vsizeperthread+threadstrock) + 2*vmargin + vmarginthreadstats + 2*vsizeperthread; 59 : 60 14 : double duration = 0; 61 613 : for(const auto& atask : tasksFinished){ 62 599 : duration = std::max(duration, startingTime.differenceWith(atask->getEndingTime())); 63 : } 64 : 65 14 : const long int hdimtime = std::max(2000, int(log(duration)*50)); 66 14 : const long int hmargin = 50; 67 14 : const long int hdim = hdimtime + 2*hmargin; 68 : 69 14 : svgfile << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; 70 14 : svgfile << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"" << hdim << "\" height=\"" << vdim << "\">\n"; 71 14 : svgfile << " <title>Execution trace</title>\n"; 72 14 : svgfile << " <desc>\n"; 73 14 : svgfile << " Spetabaru traces for " << tasksFinished.size() << " tasks\n"; 74 14 : svgfile << " </desc>\n"; 75 14 : svgfile << "\n"; 76 : // Back 77 14 : svgfile << " <rect width=\"" << hdim << "\" height=\"" << vdim << "\" x=\"0\" y=\"0\" fill=\"white\" />\n"; 78 : 79 : 80 14 : svgfile << " <line x1=\"" << hmargin << "\" y1=\"" << vmargin-10 81 14 : << "\" x2=\"" << hdimtime+hmargin << "\" y2=\"" << vmargin-10 << "\" stroke=\"" << "black" <<"\" stroke-width=\"" << "1" <<"\"/>\n"; 82 : 83 14 : svgfile << " <circle cx=\"" << hmargin << "\" cy=\"" << vmargin-10 << "\" r=\"" << "6" << "\" fill=\"" << "black" <<"\" />\n"; 84 14 : svgfile << " <circle cx=\"" << hdimtime+hmargin << "\" cy=\"" << vmargin-10 << "\" r=\"" << "6" << "\" fill=\"" << "black" << "\" />\n"; 85 : 86 20 : for(double second = 1 ; second < duration ; second += 1.){ 87 6 : svgfile << " <line x1=\"" << static_cast<long int>(double(hdimtime)*second/duration)+hmargin << "\" y1=\"" << vmargin-14 88 6 : << "\" x2=\"" << static_cast<long int>(double(hdimtime)*second/duration)+hmargin << "\" y2=\"" << vmargin-6 << "\" stroke=\"" << "black" <<"\" stroke-width=\"" << "1" <<"\"/>\n"; 89 : } 90 14 : for(double second10 = 10 ; second10 < duration ; second10 += 10.){ 91 0 : svgfile << " <line x1=\"" << static_cast<long int>(double(hdimtime)*second10/duration)+hmargin << "\" y1=\"" << vmargin-18 92 0 : << "\" x2=\"" << static_cast<long int>(double(hdimtime)*second10/duration)+hmargin << "\" y2=\"" << vmargin-2 << "\" stroke=\"" << "black" <<"\" stroke-width=\"" << "1" <<"\"/>\n"; 93 : } 94 : 95 : { 96 42 : const std::string label = "Total time = " + std::to_string(duration) + " s"; 97 14 : svgfile << "<text x=\"" << hmargin+hdimtime/2-int(label.size())*5 << "\" y=\"" << vmargin-20 << 98 14 : "\" font-size=\"30\" fill=\"black\">" << label << "</text>\n"; 99 : } 100 : 101 42 : for(auto idxThread : threadIds) { 102 28 : auto yPos = threadIdsToVerticalSlotPosMap.at(idxThread); 103 28 : svgfile << " <rect width=\"" << hdimtime << "\" height=\"" << vsizeperthread 104 28 : << "\" x=\"" << hmargin << "\" y=\"" << (yPos-1)*(vsizeperthread+threadstrock) + vmargin << "\" style=\"fill:white;stroke:black;stroke-width:" << threadstrock << "\" />\n"; 105 : 106 56 : const std::string label = "Thread " + std::to_string(idxThread); 107 : 108 28 : svgfile << "<text x=\"" << hmargin/2 << "\" y=\"" << (yPos-1)*(vsizeperthread+threadstrock) + vmargin + label.size()*30 - vsizeperthread/2 << 109 28 : "\" font-size=\"30\" fill=\"black\" transform=\"rotate(-90, " << hmargin/2 << " " 110 28 : << (yPos-1)*(vsizeperthread+threadstrock) + vmargin + label.size()*30 - vsizeperthread/2 << ")\">" << label << "</text>\n"; 111 : } 112 : 113 28 : std::unordered_map<std::string, std::string> colors; 114 : 115 613 : for(const auto& atask : tasksFinished){ 116 599 : const long int idxThreadComputer = atask->getThreadIdComputer(); 117 599 : const long int ypos_start = (threadIdsToVerticalSlotPosMap.at(idxThreadComputer)-1)*(vsizeperthread+threadstrock) + threadstrock/2 + vmargin; 118 599 : const long int ypos_end = ypos_start + vsizeperthread - threadstrock; 119 599 : const double taskStartTime = startingTime.differenceWith(atask->getStartingTime()); 120 599 : const double taskEndTime = startingTime.differenceWith(atask->getEndingTime()); 121 599 : const long int xpos_start = static_cast<long int>(double(hdimtime)*taskStartTime/duration) + hmargin; 122 599 : const long int xpos_end = std::max(xpos_start+1,static_cast<long int>(double(hdimtime)*taskEndTime/duration) + hmargin); 123 599 : const long int taskstrocke = 2; 124 : 125 1198 : std::string strForColor = atask->getTaskName(); 126 : 127 599 : const std::size_t ddpos = strForColor.find("--"); 128 599 : if(ddpos != std::string::npos){ 129 0 : strForColor = strForColor.substr(0, ddpos); 130 : } 131 599 : if(atask->getTaskName().length() && atask->getTaskName().at(atask->getTaskName().length()-1) == '\''){ 132 81 : strForColor.append("'"); 133 : } 134 : 135 599 : if(colors.find(strForColor) == colors.end()){ 136 180 : size_t hashname = std::hash<std::string>()(strForColor); 137 : 138 180 : SpMTGenerator<> randEngine(hashname); 139 180 : const int colorR = int(randEngine.getRand01()*200) + 50; 140 180 : const int colorG = int(randEngine.getRand01()*200) + 50; 141 180 : const int colorB = int(randEngine.getRand01()*200) + 50; 142 540 : colors[strForColor] = std::string("rgb(") 143 720 : + std::to_string(colorR) + "," 144 720 : + std::to_string(colorG) + "," 145 720 : + std::to_string(colorB) + ")"; 146 : } 147 : 148 599 : svgfile << "<g>\n"; 149 1797 : svgfile << " <title id=\"" << long(atask) << "\">" << SpUtils::ReplaceAllInString(SpUtils::ReplaceAllInString(SpUtils::ReplaceAllInString(atask->getTaskName(),"&", " REF "),"<","["),">","]") 150 1198 : << " -- Duration " << atask->getStartingTime().differenceWith(atask->getEndingTime()) << "s" 151 599 : << " -- Enable = " << (atask->isTaskEnabled()?"TRUE":"FALSE") << "</title>\n"; 152 : 153 599 : svgfile << " <rect width=\"" << xpos_end-xpos_start << "\" height=\"" << ypos_end-ypos_start 154 599 : << "\" x=\"" << xpos_start << "\" y=\"" << ypos_start 155 599 : << "\" style=\"fill:" << colors[strForColor] << ";stroke:black" << ";stroke-width:" << taskstrocke << "\" />\n"; 156 : 157 599 : svgfile << "</g>\n"; 158 : } 159 : 160 14 : if(showDependences){ 161 : svgfile << "<defs>\n" 162 : "<marker id=\"arrow\" markerWidth=\"20\" markerHeight=\"20\" refX=\"20\" refY=\"6\" orient=\"auto\" markerUnits=\"strokeWidth\">\n" 163 : "<path d=\"M0,0 L0,13 L20,6 z\" fill=\"gray\" />\n" 164 : "</marker>\n" 165 14 : "</defs>\n"; 166 : 167 28 : small_vector<SpAbstractTask*> deps; 168 : 169 613 : for(const auto& atask : tasksFinished){ 170 599 : atask->getDependences(&deps); 171 599 : const long int ypos_start = (threadIdsToVerticalSlotPosMap.at(atask->getThreadIdComputer())-1)*(vsizeperthread+threadstrock) + threadstrock/2 + vmargin + vsizeperthread/2; 172 599 : const double taskEndTime = startingTime.differenceWith(atask->getEndingTime()); 173 599 : const long int xpos_start = static_cast<long int>(double(hdimtime)*taskEndTime/duration) + hmargin; 174 : 175 1198 : std::set<SpAbstractTask*> alreadyExist; 176 : 177 1844 : for(const auto& taskDep : deps){ 178 1245 : if(alreadyExist.find(taskDep) == alreadyExist.end()){ 179 1245 : const long int ypos_end = (threadIdsToVerticalSlotPosMap.at(taskDep->getThreadIdComputer())-1)*(vsizeperthread+threadstrock) + threadstrock/2 + vmargin + vsizeperthread/2; 180 1245 : const long int depstrocke = 1; 181 1245 : const double taskStartTime = startingTime.differenceWith(taskDep->getStartingTime()); 182 1245 : const long int xpos_end = static_cast<long int>(double(hdimtime)*taskStartTime/duration) + hmargin; 183 : 184 1245 : svgfile << " <line x1=\"" << xpos_start << "\" y1=\"" << ypos_start 185 1245 : << "\" x2=\"" << xpos_end << "\" y2=\"" << ypos_end << "\" stroke=\"" << "gray" <<"\" stroke-width=\"" << depstrocke <<"\" marker-end=\"url(#arrow)\" />\n"; 186 : } 187 : } 188 599 : deps.clear(); 189 : } 190 : } 191 : 192 14 : const long int offsetStat = nbThreads*(vsizeperthread+threadstrock) + vmarginthreadstats; 193 14 : const char* statsNames[] = {"Submited", "Ready"}; 194 42 : for(int idxStat = 0 ; idxStat < 2 ; ++idxStat){ 195 28 : svgfile << " <rect width=\"" << hdimtime << "\" height=\"" << vsizeperthread 196 28 : << "\" x=\"" << hmargin << "\" y=\"" << offsetStat + idxStat*(vsizeperthread+threadstrock) + vmargin << "\" style=\"fill:white;stroke:black;stroke-width:" << threadstrock << "\" />\n"; 197 : 198 56 : const std::string label = statsNames[idxStat]; 199 : 200 28 : svgfile << "<text x=\"" << hmargin/2 << "\" y=\"" << offsetStat + idxStat*(vsizeperthread+threadstrock+50) + vmargin + label.size()*30 - vsizeperthread/2 << 201 28 : "\" font-size=\"30\" fill=\"black\" transform=\"rotate(-90, " << hmargin/2 << " " 202 28 : << offsetStat + idxStat*(vsizeperthread+threadstrock+50) + vmargin + label.size()*30 - vsizeperthread/2 << ")\">" << label << "</text>\n"; 203 : } 204 : 205 28 : small_vector<int> nbReady(hdimtime, 0); 206 28 : small_vector<int> nbSubmited(hdimtime, 0); 207 : 208 613 : for(const auto& atask : tasksFinished){ 209 599 : const double taskSubmitedTime = startingTime.differenceWith(atask->getCreationTime()); 210 599 : const double taskReadyTime = startingTime.differenceWith(atask->getReadyTime()); 211 599 : const double taskStartTime = startingTime.differenceWith(atask->getStartingTime()); 212 599 : const long int xpos_submited = static_cast<long int>(double(hdimtime)*taskSubmitedTime/duration); 213 599 : const long int xpos_ready = static_cast<long int>(double(hdimtime)*taskReadyTime/duration); 214 599 : const long int xpos_start = static_cast<long int>(double(hdimtime)*taskStartTime/duration); 215 : 216 599 : nbSubmited[xpos_submited] += 1; 217 : 218 599 : nbReady[xpos_ready] += 1; 219 599 : if(xpos_ready != xpos_start || xpos_ready == hdimtime-1){ 220 550 : nbReady[xpos_start] -= 1; 221 : } 222 : else{ 223 49 : nbReady[xpos_start+1] -= 1; 224 : } 225 : } 226 : 227 14 : int maxReady = 0; 228 : { 229 14 : int currentStat = 0; 230 28014 : for(int idx = 0 ; idx < hdimtime ; ++idx){ 231 28000 : currentStat += nbReady[idx]; 232 28000 : maxReady = std::max(maxReady , currentStat); 233 : } 234 : } 235 : 236 14 : const std::reference_wrapper<const small_vector<int>> statVal[] = {nbSubmited, nbReady}; 237 14 : const int maxStatVal[2] = {static_cast<int>(tasksFinished.size()), maxReady}; 238 : 239 42 : for(int idxStat = 0 ; idxStat < 2 ; ++idxStat){ 240 28 : svgfile << "<polyline points=\""; 241 : //20,20 40,25 60,40 80,120 120,140 200,180" 242 28 : const int maxStat = maxStatVal[idxStat]; 243 28 : int currentStat = 0; 244 56028 : for(int idx = 0 ; idx < hdimtime ; ++idx){ 245 56000 : currentStat += statVal[idxStat].get()[idx]; 246 56000 : const long int xpos = hmargin+idx; 247 112000 : const long int ypos = offsetStat + idxStat*(vsizeperthread+threadstrock) + vmargin 248 56000 : + vsizeperthread 249 56000 : - static_cast<long int>(double(vsizeperthread-threadstrock)*double(currentStat)/double(maxStat)) 250 : - threadstrock/2; 251 56000 : svgfile << xpos << "," << ypos << " "; 252 : } 253 28 : svgfile << "\" style=\"fill:none;stroke:rgb(112,0,0);stroke-width:3\" />\n"; 254 : } 255 : 256 14 : svgfile << "<text x=\"" << hmargin + hdimtime + 10 257 14 : << "\" y=\"" << offsetStat + 0*(vsizeperthread+threadstrock) + vmargin + 15 << 258 14 : "\" font-size=\"30\" fill=\"black\">" << tasksFinished.size() << "</text>\n"; 259 14 : svgfile << "<text x=\"" << hmargin + hdimtime + 10 260 14 : << "\" y=\"" << offsetStat + 1*(vsizeperthread+threadstrock) + vmargin + 15 << 261 14 : "\" font-size=\"30\" fill=\"black\">" << maxReady << "</text>\n"; 262 : 263 14 : svgfile << "</svg>\n"; 264 14 : } 265 : 266 : } 267 : 268 : 269 : #endif