20 #ifndef HEDGEHOG_DOT_PRINTER_H 21 #define HEDGEHOG_DOT_PRINTER_H 31 #include "abstract_printer.h" 32 #include "../../core/io/base/receiver/core_slot.h" 61 std::vector<std::string> edges_ = {};
62 std::ofstream outputFile_ = {};
69 maxExecutionTime_ = {},
70 minExecutionTime_ = {},
71 rangeExecutionTime_ = {},
75 graphExecutionDuration_ = {};
84 explicit DotPrinter(std::filesystem::path
const &dotFilePath,
88 colorScheme_(colorScheme), structureOptions_(structureOptions), debugOptions_(debugOptions) {
89 assert(graph !=
nullptr);
90 auto directoryPath = dotFilePath.parent_path();
91 if (dotFilePath.has_filename()) {
92 if (std::filesystem::exists(directoryPath)) {
93 if (std::filesystem::exists(dotFilePath)) {
95 <<
"The file " << dotFilePath.filename() <<
" will be overwritten." << std::endl;
98 <<
"The file " << dotFilePath.filename() <<
" will be created." << std::endl;
100 outputFile_ = std::ofstream(dotFilePath);
103 std::ostringstream oss;
104 oss <<
"The file " << dotFilePath.filename() <<
" can not be store in " << directoryPath
105 <<
" because the directory does not exist.";
106 HLOG_SELF(0, oss.str())
107 throw (std::runtime_error(oss.str()));
110 std::ostringstream oss;
111 oss <<
"The path: " << dotFilePath <<
" does not represent a file.";
112 HLOG_SELF(0, oss.str())
113 throw (std::runtime_error(oss.str()));
115 minExecutionTime_ = graph->minExecutionTime().count();
116 maxExecutionTime_ = graph->maxExecutionTime().count();
117 minWaitTime_ = graph->minWaitTime().count();
118 maxWaitTime_ = graph->maxWaitTime().count();
120 rangeExecutionTime_ = ((maxExecutionTime_ - minExecutionTime_) == 0 ? 1 : maxExecutionTime_ - minExecutionTime_);
121 rangeWaitTime_ = ((maxWaitTime_ - minWaitTime_) == 0 ? 1 : maxWaitTime_ - minWaitTime_);
122 graphExecutionDuration_ =
123 graph->executionDuration().count() == 0 ?
124 std::chrono::duration_cast<std::chrono::microseconds>(
125 std::chrono::high_resolution_clock::now() - graph->startExecutionTimeStamp()).count()
126 : graph->executionDuration().count();
136 if (node->type() != core::NodeType::Graph) {
141 outputFile_ << getNodeInformation(node);
145 if (node->id() == node->coreClusterNode()->id()) {
147 outputFile_ << getNodeInformation(node);
158 if (!node->isInside()) {
160 <<
"digraph " << node->id()
161 <<
" {\nlabel=\"" << node->name();
163 outputFile_ <<
" " << node->id();
165 outputFile_ <<
"\\nExecution time:" << this->durationPrinter(this->graphExecutionDuration_)
166 <<
"\\nCreation time:" << this->durationPrinter(node->creationDuration().count())
167 <<
"\"; fontsize=25; penwidth=5; ranksep=0; labelloc=top; labeljust=left; \n";
170 outputFile_ <<
"subgraph cluster" << node->id() <<
" {\nlabel=\"" << node->name();
172 outputFile_ <<
" " << node->id();
174 outputFile_ <<
"\"; fontsize=25; penwidth=5; fillcolor=white;\n";
185 outputFile_ <<
"subgraph cluster" << clusterNode->id()
186 <<
" {\nlabel=\"\"; penwidth=3; style=filled; fillcolor=\"#4e78cf63\"; color=\"#4e78cf\";\n";
188 outputFile_ <<
"box" << clusterNode->id() <<
"[label=\"\", shape=egg];\n";
198 outputFile_ <<
"}\n";
207 if (!graph->isInside()) {
209 std::copy(edges_.begin(), edges_.end(), std::ostream_iterator<std::string>(outputFile_,
"\n"));
212 outputFile_ <<
"}\n";
222 std::ostringstream ss;
223 ss <<
"box" << clusterNode->coreClusterNode()->id() <<
" -> " << clusterNode->id();
224 edges_.push_back(ss.str());
233 outputFile_ <<
"subgraph cluster" << epNode->
id() <<
" {\nlabel=\"" << epNode->
name();
236 outputFile_ <<
"\"; penwidth=1; style=dotted; style=filled; fillcolor=gray80;\n " 237 << switchNode->
id() <<
"[label=\"\", shape=triangle];\n";
243 outputFile_ <<
"}\n";
255 std::string
const &idSwitch,
256 std::string_view
const &edgeType,
257 size_t const &queueSize,
258 size_t const &maxQueueSize,
259 bool isMemoryManaged)
override {
266 penWidth =
",penwidth=1",
273 oss <<
" QS:" << queueSize <<
" MQS:" << maxQueueSize;
274 queueStr = oss.str();
279 if (isMemoryManaged) { penWidth =
",penwidth=3"; }
287 for (
auto &dest: to->
ids()) {
289 idDest =
"box" + dest.second;
291 oss << idSwitch <<
" -> " << idDest <<
"[label=\"" << edgeType << queueStr <<
"\"" 297 oss << idSwitch <<
" -> " << to->
id() <<
"[label=\"" << edgeType << queueStr <<
"\"" << penWidth <<
"];";
302 oss << idSwitch <<
" -> " << to->
id() <<
"[label=\"" << edgeType << queueStr <<
"\"" << penWidth <<
"];";
304 edges_.push_back(oss.str());
315 size_t const &queueSize,
size_t const &maxQueueSize,
316 bool isMemoryManaged)
final {
325 penWidth =
",penwidth=1";
328 if (isMemoryManaged) { penWidth =
",penwidth=3"; }
332 oss <<
" QS:" << queueSize <<
" MQS:" << maxQueueSize;
333 queueStr = oss.str();
340 if (from->isInCluster()) {
343 for (
auto &source: from->ids()) {
344 headLabel =
",ltail=cluster" + source.second;
346 if (to->isInCluster()) {
349 for (
auto &dest : to->ids()) {
350 tailLabel =
",lhead=cluster" + dest.second;
351 idDest =
"box" + dest.second;
352 oss << source.first <<
" -> " << idDest <<
"[label=\"" << edgeType << queueStr <<
"\"" << headLabel
353 << tailLabel << penWidth <<
"];";
357 oss << source.first <<
" -> " << to->id() <<
"[label=\"" << edgeType << queueStr <<
"\"" << headLabel
365 if (to->isInCluster()) {
368 for (
auto &dest : to->ids()) {
369 tailLabel =
",lhead=cluster" + dest.second;
370 idDest =
"box" + dest.second;
371 oss << from->id() <<
" -> " << idDest <<
"[label=\"" << edgeType << queueStr <<
"\"" << headLabel
372 << tailLabel << penWidth <<
"];";
376 oss << from->id() <<
" -> " << to->id() <<
"[label=\"" << edgeType << queueStr <<
"\"" << headLabel
381 edges_.push_back(oss.str());
383 }
else if (from->id() == from->coreClusterNode()->id() && to->id() == to->coreClusterNode()->id()) {
384 oss << from->id() <<
" -> " << to->id() <<
"[label=\"" << edgeType << queueStr <<
"\"" << penWidth <<
"];";
385 edges_.push_back(oss.str());
394 std::stringstream ss;
397 ss << node->
id() <<
" [label=\"" << node->
name();
400 ss <<
" " << node->
id() <<
" \\(" << node->
threadId() <<
", " << node->
graphId() <<
"\\)";
403 switch (node->
type()) {
405 case core::NodeType::Source:ss <<
"\", shape=doublecircle";
408 case core::NodeType::Sink:ss <<
"\",shape=point";
411 case core::NodeType::Task:
422 ss <<
"\\nActive input connection: " <<
dynamic_cast<core::CoreSlot *
>(node)->numberInputNodes();
426 ss <<
"\\nThread Active?: " << std::boolalpha << dynamic_cast<core::CoreSlot *>(node)->isActive();
429 ss <<
"\\nActive threads: " <<
dynamic_cast<core::CoreSlot *
>(node)->numberActiveThreadInCluster();
435 ss <<
"\\nWait Time: " << this->durationPrinter(node->
waitTime().count());
436 ss <<
"\\nExecution Time: " << this->durationPrinter(node->
executionTime().count());
437 ss <<
"\\nMemory Wait Time: " << this->durationPrinter(node->
memoryWaitTime().count());
445 ss <<
"\\nWait Time: ";
448 <<
" Min: " << this->durationPrinter(minmaxWait.first) <<
"\\n" 451 <<
" Max: " << this->durationPrinter(minmaxWait.second) <<
"\\n\\n";
456 ss <<
"Execution Time: ";
459 <<
" Min: " << this->durationPrinter(minmaxExec.first) <<
"\\n" 462 <<
" Max: " << this->durationPrinter(minmaxExec.second) <<
"\\n\\n";
469 ss <<
"Memory Wait Time: ";
472 <<
" Min: " << this->durationPrinter(minmaxMemoryWait.first) <<
"\\n" 475 <<
" Max: " << this->durationPrinter(minmaxMemoryWait.second) <<
"\\n\\n";
486 ss <<
",shape=circle";
488 switch (this->colorScheme_) {
496 if (node->
isCudaRelated()) { ss << R
"(, style=filled, fillcolor="#76b900", fontcolor="#8946ff")"; } 499 case core::NodeType::StateManager:
501 ss <<
"\\nActive input connection: " <<
dynamic_cast<core::CoreSlot *
>(node)->numberInputNodes();
502 ss <<
"\\nActive threads: " <<
dynamic_cast<core::CoreSlot *
>(node)->numberActiveThreadInCluster();
504 ss <<
"\\nWait Time: " << this->durationPrinter(node->
waitTime().count());
505 ss <<
"\\nExecution Time: " << this->durationPrinter(node->
executionTime().count());
507 ss <<
",shape=diamond";
509 switch (this->colorScheme_) {
529 std::string
getRGBFromRange(uint64_t
const &val, uint64_t
const &min, uint64_t
const &range) {
531 uint64_t posRedToBlue = (std::abs((int64_t) val - (int64_t) min)) * 255 / range;
532 posRedToBlue = posRedToBlue > 255 ? 255 : posRedToBlue;
533 std::stringstream ss;
535 << std::setfill(
'0') << std::setw(2) << std::hex << posRedToBlue
537 << std::setfill(
'0') << std::setw(2) << std::hex << 255 - posRedToBlue
540 HLOG(0,
"PRINT RGB RANGE " << val <<
" " << min <<
" " << range <<
" " << posRedToBlue)
548 return getRGBFromRange(val, this->minExecutionTime_, this->rangeExecutionTime_);
555 return getRGBFromRange(val, this->minWaitTime_, this->rangeWaitTime_);
562 std::ostringstream oss;
564 s = (duration % 1000000000) / 1000000,
565 mS = (duration % 1000000) / 1000,
566 uS = (duration % 1000);
569 oss << s <<
"." << std::setfill(
'0') << std::setw(3) << mS <<
"s";
571 oss << mS <<
"." << std::setfill(
'0') << std::setw(3) << uS <<
"ms";
573 oss << duration <<
"us";
579 #endif //HEDGEHOG_DOT_PRINTER_H NodeType type() const
Node type accessor.
std::pair< uint64_t, uint64_t > minmaxWaitTimeCluster() const
Compute and return the min and max wait time for all tasks in the node cluster.
std::chrono::duration< uint64_t, std::micro > meanMemoryWaitTimeCluster() const
Compute and return the mean memory wait time for all tasks in the node cluster.
std::string durationPrinter(uint64_t duration)
Print a duration with the good unit.
void printGraphFooter(core::CoreNode const *graph) final
Print the graph footer.
void printClusterEdge(core::CoreNode const *clusterNode) final
Print the inside edges of a cluster for a task.
std::pair< uint64_t, uint64_t > minmaxMemoryWaitTimeCluster() const
Compute and return the min and max memory wait time for all tasks in the node cluster.
virtual std::string extraPrintingInformation()
Extra printing information accessor.
bool isCudaRelated() const
Is related to CUDA, used to have a green background on the dot file.
Printer to produce a dot representation of the current state of the graph.
void printEdge(core::CoreNode const *from, core::CoreNode const *to, std::string_view const &edgeType, size_t const &queueSize, size_t const &maxQueueSize, bool isMemoryManaged) final
Create an edge between from and to.
Displays all tasks in clusters.
void printGraphHeader(core::CoreNode const *node) final
Print header for the graph.
std::string getWaitRGB(uint64_t val)
Get the rgb color for the wait time value.
StructureOptions
Enum structural options.
DotPrinter(std::filesystem::path const &dotFilePath, ColorScheme colorScheme, StructureOptions structureOptions, DebugOptions debugOptions, core::CoreNode *graph)
DotPrinter constructor, opens the file for writing.
std::chrono::duration< uint64_t, std::micro > meanExecTimeCluster() const
Compute and return the mean execution time for all tasks in the node cluster.
uint64_t stdvMemoryWaitTimeCluster() const
Compute and return the standard deviation memory wait time for all tasks in the node cluster...
DebugOptions
Enum to enable debug printing.
void printExecutionPipelineFooter() override
Print the footer for the execution pipeline.
Colors nodes based on execution time.
void printNodeInformation(core::CoreNode *node) final
Print node main information.
Slot interface, receive notification from CoreNotifier.
Displays both ALLTHREADING and QUEUE.
int threadId() const
Thread id accessor.
virtual int graphId()
Graph id accessor.
Shows debug information such as pointer addresses for nodes and edges.
uint64_t stdvWaitTimeCluster() const
Compute and return the standard deviation wait time for all tasks in the node cluster.
std::pair< uint64_t, uint64_t > minmaxExecTimeCluster() const
Compute and return the min and max execution time for all tasks in the node cluster.
void printEdgeSwitchGraphs(core::CoreNode *to, std::string const &idSwitch, std::string_view const &edgeType, size_t const &queueSize, size_t const &maxQueueSize, bool isMemoryManaged) override
Print an edge between the switch and a graph's input node.
CoreNode * coreClusterNode() const
Main cluster core node link to this node accessor.
std::string getRGBFromRange(uint64_t const &val, uint64_t const &min, uint64_t const &range)
Return a RGB color for a value knowing the minimum value and the range, blue least, red highest.
Main Hedgehog core abstraction.
uint64_t stdvExecTimeCluster() const
Compute and return the standard deviation execution time for all tasks in the node cluster...
void printExecutionPipelineHeader(core::CoreNode *epNode, core::CoreNode *switchNode) override
Print the execution pipeline header.
std::chrono::duration< uint64_t, std::micro > meanWaitTimeCluster() const
Compute and return the mean wait time for all tasks in the node cluster.
bool isInCluster() const
In cluster property accessor.
~DotPrinter() override
Destructor, close the file.
std::string_view const & name() const
Node name accessor.
std::chrono::duration< uint64_t, std::micro > const & memoryWaitTime() const
Memory wait time accessor.
Displays queue details (max queue size and queue size along edges)
size_t numberThreads() const
Number of threads associated accessor.
void printClusterHeader(core::CoreNode const *clusterNode) final
Print the header of a cluster.
ColorScheme
Enum color options.
std::string getExecRGB(uint64_t val)
Get the rgb color for the execution time value.
void printClusterFooter() final
Print the footer of a task cluster.
std::chrono::duration< uint64_t, std::micro > const & executionTime() const
Execution time accessor.
Colors nodes based on wait time.
virtual std::vector< std::pair< std::string, std::string > > ids() const
Input node ids [nodeId, nodeIdCluster] accessor.
virtual std::string id() const
Unique Id accessor.
std::chrono::duration< uint64_t, std::micro > const & waitTime() const
Wait time accessor.
std::string getNodeInformation(core::CoreNode *node)
Extract and create node information.