From 146652949b9b958d95d0729a6326f013c77795b5 Mon Sep 17 00:00:00 2001 From: kstppd Date: Wed, 10 Sep 2025 00:50:13 +0300 Subject: [PATCH 1/5] flamegraph data file --- src/paralleltimertree.cpp | 63 ++++++++++++++++++++++++++++++++++++++- src/paralleltimertree.hpp | 2 ++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/paralleltimertree.cpp b/src/paralleltimertree.cpp index 3036286e..9a5095c9 100644 --- a/src/paralleltimertree.cpp +++ b/src/paralleltimertree.cpp @@ -31,7 +31,7 @@ License along with this library. If not, see . #include "paralleltimertree.hpp" #include "prettyprinttable.hpp" #include "common.hpp" - +#include #ifdef _OPENMP #include "omp.h" #endif @@ -490,6 +490,60 @@ bool ParallelTimerTree::printTimers(double minFraction, const std::map &elements, + const std::string &delim) -> std::string { + if (elements.empty()) { + return ""; + } + return std::accumulate( + std::next(elements.begin()), elements.end(), elements[0], + [&delim](const std::string &a, const std::string &b) { + return a + delim + b; + }); + }; + + if (rankInPrint == 0) { + std::ofstream f(filename); + if (!f.is_open()) { + std::cerr << "Error: Could not open file for writing flamegraph data: " + << filename << std::endl; + return false; + } + std::vector stack; + for (unsigned int i = 1; i < stats.id.size(); i++) { + const int id = stats.id[i]; + const int level = stats.level[i]; + std::string label; + if (id != -1) { + label = (*this)[id].getLabel(); + } else { + label = "Uknown element"; + } + double avg_seconds = 0.0; + if (nProcessesInPrint > 0) { + avg_seconds = stats.timeSum[i] / nProcessesInPrint; + } + const std::size_t value = static_cast(avg_seconds * 1.0e6); + if (value <= 0) { + continue; + } + if (level > 0) { + stack.resize(level - 1); + } else { + stack.clear(); + } + stack.push_back(label); + f << join(stack, ";") << " " << value << "\n"; + } + f.close(); + } + return true; +} //print out global timers //If any labels differ, then this print will deadlock. Only call it with a communicator that is guaranteed to be consistent on all processes. @@ -736,6 +790,13 @@ bool ParallelTimerTree::print(MPI_Comm communicator, std::string fileNamePrefix) } output.close(); } + //Generate detailed flamegraph file + std::stringstream fname_flamegraph; + fname_flamegraph << fileNamePrefix << "_flamegraph"<< ".txt"; + if (!generateFlameGraphDataFile(fname_flamegraph.str())){ + std::cerr << "ERROR: Failed to generate flamegraph file!" << std::endl; + } + MPI_Comm_free(&printComm); } diff --git a/src/paralleltimertree.hpp b/src/paralleltimertree.hpp index 1c3a6ba7..4370d694 100644 --- a/src/paralleltimertree.hpp +++ b/src/paralleltimertree.hpp @@ -114,6 +114,8 @@ class ParallelTimerTree: public TimerTree { const std::map &groupIds, std::ofstream &output); + bool generateFlameGraphDataFile(const std::string &outputPath); + bool printGroupStatistics(double minFraction, const std::map &groupIds, std::ofstream &output); From 09c8ffe9bd1ec86ca2f94dd3290b8b4b57af2420 Mon Sep 17 00:00:00 2001 From: kstppd Date: Wed, 10 Sep 2025 11:25:40 +0300 Subject: [PATCH 2/5] use exclusive times --- src/paralleltimertree.cpp | 63 ++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/paralleltimertree.cpp b/src/paralleltimertree.cpp index 9a5095c9..49657163 100644 --- a/src/paralleltimertree.cpp +++ b/src/paralleltimertree.cpp @@ -493,51 +493,72 @@ bool ParallelTimerTree::printTimers(double minFraction, const std::map &elements, const std::string &delim) -> std::string { if (elements.empty()) { return ""; } - return std::accumulate( - std::next(elements.begin()), elements.end(), elements[0], - [&delim](const std::string &a, const std::string &b) { - return a + delim + b; - }); + std::string result = elements[0]; + for (std::size_t i = 1; i < elements.size(); ++i) { + result += delim + elements[i]; + } + return result; }; if (rankInPrint == 0) { + if (stats.id.empty()) { + return true; + } + + constexpr std::size_t max_loops = 1000; + std::vector exclusiveTimeSum = stats.timeSum; + std::vector parentIndexStack; + // Calculate exclusive timers for flamegraph by removing children + for (std::size_t i = 1; i < stats.id.size(); ++i) { + const int currentLevel = stats.level[i]; + while (!parentIndexStack.empty() && + stats.level[parentIndexStack.back()] >= currentLevel) { + counter++; + parentIndexStack.pop_back(); + } + + if (!parentIndexStack.empty()) { + int parentIndex = parentIndexStack.back(); + exclusiveTimeSum[parentIndex] -= stats.timeSum[i]; + } + parentIndexStack.push_back(i); + } + std::ofstream f(filename); if (!f.is_open()) { std::cerr << "Error: Could not open file for writing flamegraph data: " << filename << std::endl; return false; } + std::vector stack; - for (unsigned int i = 1; i < stats.id.size(); i++) { + for (unsigned int i = 0; i < stats.id.size(); i++) { const int id = stats.id[i]; const int level = stats.level[i]; + + if (level < 0) { + continue; + } + std::string label; if (id != -1) { label = (*this)[id].getLabel(); } else { - label = "Uknown element"; - } - double avg_seconds = 0.0; - if (nProcessesInPrint > 0) { - avg_seconds = stats.timeSum[i] / nProcessesInPrint; - } - const std::size_t value = static_cast(avg_seconds * 1.0e6); - if (value <= 0) { - continue; - } - if (level > 0) { - stack.resize(level - 1); - } else { - stack.clear(); + label = "Unknown element"; } + + stack.resize(level); stack.push_back(label); + + auto value = 1.0e3 * exclusiveTimeSum[i]; f << join(stack, ";") << " " << value << "\n"; } f.close(); From eada7c634c9c5bb4e31825ad80e0e0fb8a334a4c Mon Sep 17 00:00:00 2001 From: kstppd Date: Wed, 10 Sep 2025 11:28:18 +0300 Subject: [PATCH 3/5] use exclusive times --- src/paralleltimertree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/paralleltimertree.cpp b/src/paralleltimertree.cpp index 49657163..36899fb9 100644 --- a/src/paralleltimertree.cpp +++ b/src/paralleltimertree.cpp @@ -521,7 +521,6 @@ bool ParallelTimerTree::generateFlameGraphDataFile( const int currentLevel = stats.level[i]; while (!parentIndexStack.empty() && stats.level[parentIndexStack.back()] >= currentLevel) { - counter++; parentIndexStack.pop_back(); } From a6a5982c1e3b215ee9b7f7a715e41588ca075b64 Mon Sep 17 00:00:00 2001 From: kstppd Date: Wed, 10 Sep 2025 11:28:52 +0300 Subject: [PATCH 4/5] remove unused max loops --- src/paralleltimertree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/paralleltimertree.cpp b/src/paralleltimertree.cpp index 36899fb9..6af43910 100644 --- a/src/paralleltimertree.cpp +++ b/src/paralleltimertree.cpp @@ -513,7 +513,6 @@ bool ParallelTimerTree::generateFlameGraphDataFile( return true; } - constexpr std::size_t max_loops = 1000; std::vector exclusiveTimeSum = stats.timeSum; std::vector parentIndexStack; // Calculate exclusive timers for flamegraph by removing children From f7e8fe90e7dba8a68c8c7a054031bd7fe3e7fac5 Mon Sep 17 00:00:00 2001 From: kstppd Date: Wed, 10 Sep 2025 11:45:32 +0300 Subject: [PATCH 5/5] remove numeric include --- src/paralleltimertree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/paralleltimertree.cpp b/src/paralleltimertree.cpp index 6af43910..e294f846 100644 --- a/src/paralleltimertree.cpp +++ b/src/paralleltimertree.cpp @@ -31,7 +31,6 @@ License along with this library. If not, see . #include "paralleltimertree.hpp" #include "prettyprinttable.hpp" #include "common.hpp" -#include #ifdef _OPENMP #include "omp.h" #endif