diff --git a/src/paralleltimertree.cpp b/src/paralleltimertree.cpp index 3036286e..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" - #ifdef _OPENMP #include "omp.h" #endif @@ -490,6 +489,79 @@ bool ParallelTimerTree::printTimers(double minFraction, const std::map &elements, + const std::string &delim) -> std::string { + if (elements.empty()) { + return ""; + } + 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; + } + + 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) { + 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 = 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 = "Unknown element"; + } + + stack.resize(level); + stack.push_back(label); + + auto value = 1.0e3 * exclusiveTimeSum[i]; + 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 +808,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);