0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-27 08:41:02 +08:00

(GUI) Added statistics gathering for context switches (calls number, total duration, % per thread or frame).

Known issue: if context switch events occur out of profiled blocks then percent/thread may be calculated wrong as it is calculating relative to thread profiled time (which may be a very low value).
This commit is contained in:
Victor Zarubkin 2016-12-14 23:17:02 +03:00
parent 8cfeb1b7f2
commit d85f9864d6
2 changed files with 119 additions and 8 deletions

View File

@ -188,11 +188,14 @@ typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics
which uses it as a key, exists only inside fillTreesFromFile function. */ which uses it as a key, exists only inside fillTreesFromFile function. */
typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> IdMap; typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> IdMap;
typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::BlockStatistics*> CsStatsMap;
#else #else
// TODO: Create optimized version of profiler::hashed_cstr for Linux too. // TODO: Create optimized version of profiler::hashed_cstr for Linux too.
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap; typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap;
typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap; typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap;
typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::BlockStatistics*> CsStatsMap;
#endif #endif
@ -253,6 +256,47 @@ automatically receive statistics update.
return stats; return stats;
} }
::profiler::BlockStatistics* update_statistics(CsStatsMap& _stats_map, const ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index)
{
auto duration = _current.node->duration();
CsStatsMap::key_type key(_current.node->name());
auto it = _stats_map.find(key);
if (it != _stats_map.end())
{
// Update already existing statistics
auto stats = it->second; // write pointer to statistics into output (this is BlocksTree:: per_thread_stats or per_parent_stats or per_frame_stats)
++stats->calls_number; // update calls number of this block
stats->total_duration += duration; // update summary duration of all block calls
if (duration > stats->max_duration)
{
// update max duration
stats->max_duration_block = _current_index;
stats->max_duration = duration;
}
if (duration < stats->min_duration)
{
// update min duraton
stats->min_duration_block = _current_index;
stats->min_duration = duration;
}
// average duration is calculated inside average_duration() method by dividing total_duration to the calls_number
return stats;
}
// This is first time the block appear in the file.
// Create new statistics.
auto stats = new ::profiler::BlockStatistics(duration, _current_index, _parent_index);
_stats_map.emplace(key, stats);
return stats;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void update_statistics_recursive(StatsMap& _stats_map, ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, ::profiler::blocks_t& _blocks) void update_statistics_recursive(StatsMap& _stats_map, ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, ::profiler::blocks_t& _blocks)
@ -462,7 +506,7 @@ extern "C" {
} }
typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats; typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats;
PerThreadStats thread_statistics, parent_statistics, frame_statistics; PerThreadStats parent_statistics, frame_statistics;
IdMap identification_table; IdMap identification_table;
blocks.reserve(total_blocks_number); blocks.reserve(total_blocks_number);
@ -492,6 +536,8 @@ extern "C" {
root.thread_name = name.data(); root.thread_name = name.data();
} }
CsStatsMap per_thread_statistics_cs;
uint32_t blocks_number_in_thread = 0; uint32_t blocks_number_in_thread = 0;
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
auto threshold = read_number + blocks_number_in_thread; auto threshold = read_number + blocks_number_in_thread;
@ -534,6 +580,12 @@ extern "C" {
root.wait_time += baseData->duration(); root.wait_time += baseData->duration();
root.sync.emplace_back(block_index); root.sync.emplace_back(block_index);
if (gather_statistics)
{
EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral);
tree.per_thread_stats = update_statistics(per_thread_statistics_cs, tree, block_index, thread_id);
}
} }
auto oldprogress = progress.exchange(20 + static_cast<int>(70 * i / memory_size), ::std::memory_order_release); auto oldprogress = progress.exchange(20 + static_cast<int>(70 * i / memory_size), ::std::memory_order_release);
@ -547,6 +599,8 @@ extern "C" {
if (inFile.eof()) if (inFile.eof())
break; break;
StatsMap per_thread_statistics;
blocks_number_in_thread = 0; blocks_number_in_thread = 0;
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
threshold = read_number + blocks_number_in_thread; threshold = read_number + blocks_number_in_thread;
@ -600,9 +654,6 @@ extern "C" {
tree.node = baseData; tree.node = baseData;
const auto block_index = blocks_counter++; const auto block_index = blocks_counter++;
auto& per_parent_statistics = parent_statistics[thread_id];
auto& per_thread_statistics = thread_statistics[thread_id];
if (*tree.node->name() != 0) if (*tree.node->name() != 0)
{ {
// If block has runtime name then generate new id for such block. // If block has runtime name then generate new id for such block.
@ -649,6 +700,7 @@ extern "C" {
if (gather_statistics) if (gather_statistics)
{ {
EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta); EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta);
auto& per_parent_statistics = parent_statistics[thread_id];
per_parent_statistics.clear(); per_parent_statistics.clear();
//per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows //per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows
@ -731,6 +783,7 @@ extern "C" {
// return blocks[left].node->begin() < blocks[right].node->begin(); // return blocks[left].node->begin() < blocks[right].node->begin();
//}); //});
::profiler::block_index_t cs_index = 0;
for (auto i : root.children) for (auto i : root.children)
{ {
auto& frame = blocks[i]; auto& frame = blocks[i];
@ -739,6 +792,22 @@ extern "C" {
per_frame_statistics.clear(); per_frame_statistics.clear();
update_statistics_recursive(per_frame_statistics, frame, i, i, blocks); update_statistics_recursive(per_frame_statistics, frame, i, i, blocks);
if (cs_index < root.sync.size())
{
CsStatsMap frame_stats_cs;
do {
auto j = root.sync[cs_index];
auto& cs = blocks[j];
if (cs.node->end() < frame.node->begin())
continue;
if (cs.node->begin() > frame.node->end())
break;
cs.per_frame_stats = update_statistics(frame_stats_cs, cs, cs_index, i);
} while (++cs_index < root.sync.size());
}
if (root.depth < frame.depth) if (root.depth < frame.depth)
root.depth = frame.depth; root.depth = frame.depth;

View File

@ -1501,27 +1501,69 @@ void EasyGraphicsView::onIdleTimeout()
auto cse = item->intersectEvent(pos); auto cse = item->intersectEvent(pos);
if (cse) if (cse)
{ {
const auto& itemBlock = cse->tree;
auto widget = new QWidget(); auto widget = new QWidget();
auto lay = new QGridLayout(widget); auto lay = new QGridLayout(widget);
int row = 0; int row = 0;
lay->addWidget(new EasyBoldLabel("Context switch event", widget), row, 0, 1, 2, Qt::AlignHCenter); lay->addWidget(new EasyBoldLabel("Context switch event", widget), row, 0, 1, 2, Qt::AlignHCenter);
++row; ++row;
lay->addWidget(new QLabel("Thread:", widget), row, 0, Qt::AlignRight); lay->addWidget(new QLabel("Thread:", widget), row, 0, Qt::AlignRight);
auto it = EASY_GLOBALS.profiler_blocks.find(cse->tree.node->id()); auto it = EASY_GLOBALS.profiler_blocks.find(cse->tree.node->id());
if (it != EASY_GLOBALS.profiler_blocks.end()) if (it != EASY_GLOBALS.profiler_blocks.end())
lay->addWidget(new QLabel(QString("%1 %2").arg(cse->tree.node->id()).arg(it->second.name()), widget), row, 1, Qt::AlignLeft); lay->addWidget(new QLabel(QString("%1 %2").arg(cse->tree.node->id()).arg(it->second.name()), widget), row, 1, Qt::AlignLeft);
else else
lay->addWidget(new QLabel(QString::number(cse->tree.node->id()), widget), row, 1, Qt::AlignLeft); lay->addWidget(new QLabel(QString::number(cse->tree.node->id()), widget), row, 1, Qt::AlignLeft);
++row; ++row;
lay->addWidget(new QLabel("Process:", widget), row, 0, Qt::AlignRight); lay->addWidget(new QLabel("Process:", widget), row, 0, Qt::AlignRight);
lay->addWidget(new QLabel(cse->tree.node->name(), widget), row, 1, Qt::AlignLeft); lay->addWidget(new QLabel(cse->tree.node->name(), widget), row, 1, Qt::AlignLeft);
++row; ++row;
const auto duration = itemBlock.node->duration();
lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight); lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight);
lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, cse->tree.node->duration(), 3), widget), row, 1, Qt::AlignLeft); lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, Qt::AlignLeft);
++row;
if (itemBlock.per_thread_stats)
{
lay->addWidget(new QLabel("Sum:", widget), row, 0, Qt::AlignRight);
lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, itemBlock.per_thread_stats->total_duration, 3), widget), row, 1, 1, 3, Qt::AlignLeft);
++row;
lay->addWidget(new EasyBoldLabel("-------- Statistics --------", widget), row, 0, 1, 5, Qt::AlignHCenter);
lay->addWidget(new QLabel("per ", widget), row + 1, 0, Qt::AlignRight);
lay->addWidget(new QLabel("This %:", widget), row + 2, 0, Qt::AlignRight);
lay->addWidget(new QLabel("Sum %:", widget), row + 3, 0, Qt::AlignRight);
lay->addWidget(new QLabel("N Calls:", widget), row + 4, 0, Qt::AlignRight);
lay->addWidget(new QLabel("Thread", widget), row + 1, 1, Qt::AlignHCenter);
auto percent = ::profiler_gui::percentReal(duration, item->root()->profiled_time);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), row + 2, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->profiled_time)), widget), row + 3, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row + 4, 1, Qt::AlignHCenter);
if (itemBlock.per_frame_stats)
{
int col = 2;
auto frame_duration = blocksTree(itemBlock.per_frame_stats->parent_block).node->duration();
lay->addWidget(new QLabel("Frame", widget), row + 1, col, Qt::AlignHCenter);
percent = ::profiler_gui::percentReal(duration, frame_duration);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), row + 2, col, Qt::AlignHCenter);
percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration, frame_duration);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), row + 3, col, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 4, col, Qt::AlignHCenter);
}
}
m_csInfoWidget = new QGraphicsProxyWidget(); m_csInfoWidget = new QGraphicsProxyWidget();
m_csInfoWidget->setWidget(widget); m_csInfoWidget->setWidget(widget);