LCOV - code coverage report
Current view: top level - Src/Output - SpSvgTrace.hpp (source / functions) Hit Total Coverage
Test: Coverage example Lines: 159 163 97.5 %
Date: 2021-12-02 17:21:05 Functions: 4 4 100.0 %

          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

Generated by: LCOV version 1.14