29#include "rt_graph.hpp"
50 StatFormat(Stat stat_) : stat(stat_) {
72 case Stat::QuartileHigh:
73 header =
"Quartile High";
76 case Stat::QuartileLow:
77 header =
"Quartile Low";
88 case Stat::Percentage:
92 case Stat::ParentPercentage:
96 case Stat::SelfPercentage:
108auto unit_prefix(
const double value) -> std::pair<double, char> {
109 if (value == 0.0 || value == -0.0) {
110 return {
value,
'\0'};
113 const double exponent = std::log10(std::abs(value));
114 const int siExponent =
static_cast<int>(std::floor(exponent / 3.0)) * 3;
117 switch (siExponent) {
172 return {
value * std::pow(10.0,
static_cast<double>(-siExponent)), prefix};
176auto format_time(
const double time_seconds) -> std::string {
177 if (time_seconds <= 0.0)
return std::string(
"0.00 s ");
181 std::tie(value, prefix) = unit_prefix(time_seconds);
183 std::stringstream result;
184 result << std::fixed << std::setprecision(2);
187 if (prefix) result << prefix;
189 if (!prefix) result <<
" ";
193auto calc_median(
const std::vector<double>::const_iterator& begin,
194 const std::vector<double>::const_iterator& end) ->
double {
195 const auto n = end - begin;
196 if (n == 0)
return 0.0;
198 return (*(begin + n / 2) + *(begin + n / 2 - 1)) / 2.0;
200 return *(begin + n / 2);
204auto print_stat(std::ostream& out,
const StatFormat& format,
205 const std::vector<double>& sortedTimings,
double totalSum,
double parentSum,
206 double currentSum,
double subSum) ->
void {
207 switch (format.stat) {
209 if (sortedTimings.size() >= 100000) {
212 std::tie(value, prefix) = unit_prefix(sortedTimings.size());
213 out << std::right << std::setw(format.space)
214 << std::to_string(
static_cast<int>(value)) + prefix;
216 out << std::right << std::setw(format.space) << sortedTimings.size();
220 out << std::right << std::setw(format.space) << format_time(currentSum);
223 out << std::right << std::setw(format.space)
224 << format_time(std::max<double>(currentSum - subSum, 0.0));
227 out << std::right << std::setw(format.space)
228 << format_time(currentSum / sortedTimings.size());
231 out << std::right << std::setw(format.space)
232 << format_time(calc_median(sortedTimings.begin(), sortedTimings.end()));
234 case Stat::QuartileHigh: {
235 const double upperQuartile =
236 calc_median(sortedTimings.begin() + sortedTimings.size() / 2 +
237 (sortedTimings.size() % 2) * (sortedTimings.size() > 1),
238 sortedTimings.end());
239 out << std::right << std::setw(format.space) << format_time(upperQuartile);
241 case Stat::QuartileLow: {
242 const double lowerQuartile =
243 calc_median(sortedTimings.begin(), sortedTimings.begin() + sortedTimings.size() / 2);
244 out << std::right << std::setw(format.space) << format_time(lowerQuartile);
247 out << std::right << std::setw(format.space) << format_time(sortedTimings.front());
250 out << std::right << std::setw(format.space) << format_time(sortedTimings.back());
252 case Stat::Percentage: {
254 (totalSum < currentSum || totalSum == 0) ? 100.0 : currentSum / totalSum * 100.0;
255 out << std::right << std::fixed << std::setprecision(2) << std::setw(format.space) << p;
257 case Stat::ParentPercentage: {
259 (parentSum < currentSum || parentSum == 0) ? 100.0 : currentSum / parentSum * 100.0;
260 out << std::right << std::fixed << std::setprecision(2) << std::setw(format.space) << p;
262 case Stat::SelfPercentage: {
263 const double p = (currentSum == 0)
265 : std::max<double>(currentSum - subSum, 0.0) / currentSum * 100.0;
266 out << std::right << std::fixed << std::setprecision(2) << std::setw(format.space) << p;
272struct TimeStampPair {
273 std::string identifier;
275 double startTime = 0.0;
276 std::size_t startIdx = 0;
277 std::size_t stopIdx = 0;
278 internal::TimingNode* nodePtr =
nullptr;
282auto print_node(
const std::size_t level, std::ostream& out,
283 const std::vector<internal::StatFormat> formats, std::size_t
const identifierSpace,
284 std::string nodePrefix,
const internal::TimingNode& node,
bool isLastSubnode,
285 double parentTime,
double totalTime) ->
void {
288 std::string identifier = nodePrefix;
291 out << std::setw(identifierSpace + 2);
293 identifier +=
"\u2514 ";
295 identifier +=
"\u251c ";
297 out << std::setw(identifierSpace);
299 identifier += node.identifier;
302 auto sortedTimings = node.timings;
303 std::sort(sortedTimings.begin(), sortedTimings.end());
305 double subTime = 0.0;
306 for (
const auto& subNode : node.subNodes) {
307 subTime += subNode.totalTime;
309 for (
const auto& format : formats) {
310 print_stat(out, format, sortedTimings, totalTime, parentTime, node.totalTime, subTime);
313 for (
const auto& subNode : node.subNodes) {
314 std::string newNodePrefix = nodePrefix;
315 std::size_t newIdentifierSpace = identifierSpace;
318 newNodePrefix +=
" ";
320 newNodePrefix +=
"\u2502 ";
322 newIdentifierSpace += 2;
325 print_node(level + 1, out, formats, newIdentifierSpace, newNodePrefix, subNode,
326 &subNode == &node.subNodes.back(), node.totalTime, totalTime);
331auto max_node_identifier_length(
const internal::TimingNode& node,
const std::size_t recursionDepth,
332 const std::size_t addPerLevel,
const std::size_t parentMax)
334 std::size_t currentLength = node.identifier.length() + recursionDepth * addPerLevel;
335 std::size_t max = currentLength > parentMax ? currentLength : parentMax;
336 for (
const auto& subNode : node.subNodes) {
337 const std::size_t subMax =
338 max_node_identifier_length(subNode, recursionDepth + 1, addPerLevel, max);
339 if (subMax > max) max = subMax;
345auto export_node_json(
const std::string& padding,
const std::list<internal::TimingNode>& nodeList,
346 std::ostream&
stream) ->
void {
347 stream <<
"{" << std::endl;
348 const std::string nodePadding = padding +
" ";
349 const std::string subNodePadding = nodePadding +
" ";
350 for (
const auto& node : nodeList) {
351 stream << nodePadding <<
"\"" << node.identifier <<
"\" : {" << std::endl;
352 stream << subNodePadding <<
"\"timings\" : [";
353 for (
const auto& value : node.timings) {
355 if (&value != &(node.timings.back()))
stream <<
", ";
357 stream <<
"]," << std::endl;
358 stream << subNodePadding <<
"\"start-times\" : [";
359 for (
const auto& value : node.startTimes) {
361 if (&value != &(node.startTimes.back()))
stream <<
", ";
363 stream <<
"]," << std::endl;
364 stream << subNodePadding <<
"\"sub-timings\" : ";
365 export_node_json(subNodePadding, node.subNodes,
stream);
366 stream << nodePadding <<
"}";
367 if (&node != &(nodeList.back()))
stream <<
",";
370 stream << padding <<
"}" << std::endl;
373auto extract_timings(
const std::string& identifier,
const std::list<TimingNode>& nodes,
374 std::vector<double>& timings) ->
void {
375 for (
const auto& node : nodes) {
376 if (node.identifier == identifier) {
377 timings.insert(timings.end(), node.timings.begin(), node.timings.end());
379 extract_timings(identifier, node.subNodes, timings);
383auto sort_timings_nodes(std::list<TimingNode>& nodes) ->
void {
385 nodes.sort([](
const TimingNode& n1,
const TimingNode& n2) ->
bool {
386 return n1.totalTime > n2.totalTime;
388 for (
auto& n : nodes) {
389 sort_timings_nodes(n.subNodes);
393auto flatten_timings_nodes(std::list<TimingNode>& rootNodes, std::list<TimingNode>& nodes) ->
void {
395 for (
auto& n : nodes) {
396 flatten_timings_nodes(rootNodes, n.subNodes);
399 for (
auto& n : nodes) {
400 auto it = std::find_if(
401 rootNodes.begin(), rootNodes.end(),
402 [&n](
const TimingNode& element) ->
bool { return element.identifier == n.identifier; });
403 if (it == rootNodes.end()) {
405 rootNodes.emplace_back(std::move(n));
408 it->timings.insert(it->timings.end(), n.timings.begin(), n.timings.end());
409 it->startTimes.insert(it->startTimes.end(), n.startTimes.begin(), n.startTimes.end());
410 it->totalTime += n.totalTime;
418auto flatten_timings_nodes_from_level(std::list<TimingNode>& nodes, std::size_t targetLevel,
419 std::size_t currentLevel) ->
void {
420 if (targetLevel > currentLevel) {
421 for (
auto& n : nodes) {
422 flatten_timings_nodes_from_level(n.subNodes, targetLevel, currentLevel + 1);
425 for (
auto& n : nodes) {
426 flatten_timings_nodes(nodes, n.subNodes);
437auto Timer::process() const -> TimingResult {
438 std::list<internal::TimingNode> results;
439 std::stringstream warnings;
442 std::vector<internal::TimeStampPair> timePairs;
443 timePairs.reserve(timeStamps_.size() / 2);
446 for (std::size_t i = 0; i < timeStamps_.size(); ++i) {
447 if (timeStamps_[i].type == internal::TimeStampType::Start) {
448 internal::TimeStampPair pair;
450 pair.identifier = std::string(timeStamps_[i].identifierPtr);
451 std::size_t numInnerMatchingIdentifiers = 0;
453 for (std::size_t j = i + 1; j < timeStamps_.size(); ++j) {
455 if (std::string(timeStamps_[j].identifierPtr) ==
456 std::string(timeStamps_[i].identifierPtr)) {
457 if (timeStamps_[j].type == internal::TimeStampType::Stop &&
458 numInnerMatchingIdentifiers == 0) {
460 std::chrono::duration<double> duration = timeStamps_[j].time - timeStamps_[i].time;
461 pair.time = duration.count();
462 duration = timeStamps_[i].time - timeStamps_[0].time;
463 pair.startTime = duration.count();
465 timePairs.push_back(pair);
466 if (pair.time < 0 || pair.startTime < 0) {
467 warnings <<
"rt_graph WARNING:Measured time is negative. Non-steady system-clock?!"
471 }
else if (timeStamps_[j].type == internal::TimeStampType::Stop &&
472 numInnerMatchingIdentifiers > 0) {
474 --numInnerMatchingIdentifiers;
475 }
else if (timeStamps_[j].type == internal::TimeStampType::Start) {
477 ++numInnerMatchingIdentifiers;
481 if (pair.stopIdx == 0) {
482 warnings <<
"rt_graph WARNING: Start / stop time stamps do not match for \""
483 << timeStamps_[i].identifierPtr <<
"\"!" << std::endl;
490 for (std::size_t i = 0; i < timePairs.size(); ++i) {
491 auto& pair = timePairs[i];
495 for (
auto timePairIt = timePairs.rbegin() + (timePairs.size() - i);
496 timePairIt != timePairs.rend(); ++timePairIt) {
497 if (timePairIt->stopIdx > pair.stopIdx && timePairIt->nodePtr !=
nullptr) {
498 auto& parentNode = *(timePairIt->nodePtr);
500 bool nodeFound =
false;
501 for (
auto& subNode : parentNode.subNodes) {
502 if (subNode.identifier == pair.identifier) {
504 subNode.add_time(pair.startTime, pair.time);
506 pair.nodePtr = &(subNode);
512 internal::TimingNode newNode;
513 newNode.identifier = pair.identifier;
514 newNode.add_time(pair.startTime, pair.time);
515 parentNode.subNodes.push_back(std::move(newNode));
517 pair.nodePtr = &(parentNode.subNodes.back());
524 if (pair.nodePtr ==
nullptr) {
526 for (
auto& topNode : results) {
527 if (topNode.identifier == pair.identifier) {
528 topNode.add_time(pair.startTime, pair.time);
529 pair.nodePtr = &(topNode);
536 if (pair.nodePtr ==
nullptr) {
537 internal::TimingNode newNode;
538 newNode.identifier = pair.identifier;
539 newNode.add_time(pair.startTime, pair.time);
541 results.push_back(std::move(newNode));
544 pair.nodePtr = &(results.back());
547 }
catch (
const std::exception& e) {
548 warnings <<
"rt_graph WARNING: Processing of timings failed: " << e.what() << std::endl;
550 warnings <<
"rt_graph WARNING: Processing of timings failed!" << std::endl;
553 return TimingResult(std::move(results), warnings.str());
556auto TimingResult::json() const -> std::
string {
557 std::stringstream jsonStream;
558 jsonStream << std::scientific;
559 internal::export_node_json(
"", rootNodes_, jsonStream);
560 return jsonStream.str();
563auto TimingResult::get_timings(
const std::string& identifier)
const -> std::vector<double> {
564 std::vector<double> timings;
565 internal::extract_timings(identifier, rootNodes_, timings);
569auto TimingResult::print(std::vector<Stat> statistic)
const -> std::string {
576 std::size_t identifierSpace = 0;
577 for (
const auto& node : rootNodes_) {
578 const auto nodeMax = internal::max_node_identifier_length(node, 0, 2, identifierSpace);
579 if (nodeMax > identifierSpace) identifierSpace = nodeMax;
582 auto totalSpace = identifierSpace;
584 std::vector<internal::StatFormat> formats;
585 formats.reserve(statistic.size());
586 for (
const auto& stat : statistic) {
587 formats.emplace_back(stat);
588 totalSpace += formats.back().space;
594 stream << std::string(totalSpace,
'=') << std::endl;
597 stream << std::right << std::setw(identifierSpace) <<
"";
598 for (
const auto& format : formats) {
599 stream << std::right << std::setw(format.space) << format.header;
604 stream << std::string(totalSpace,
'-') << std::endl;
607 for (
const auto& node : rootNodes_) {
608 internal::print_node(0,
stream, formats, identifierSpace,
"", node,
true, node.totalTime,
614 stream << std::string(totalSpace,
'=') << std::endl;
619auto TimingResult::flatten(std::size_t level) -> TimingResult& {
620 internal::flatten_timings_nodes_from_level(rootNodes_, level, 0);
624auto TimingResult::sort_nodes() -> TimingResult& {
625 internal::sort_timings_nodes(rootNodes_);
@ value
the parser finished reading a JSON value
acc_stream_t stream(stream_id sid__)
Return a single device stream.