0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-26 16:11: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. */
typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> IdMap;
typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::BlockStatistics*> CsStatsMap;
#else
// 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::hashed_stdstring, ::profiler::block_id_t> IdMap;
typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::BlockStatistics*> CsStatsMap;
#endif
@ -253,6 +256,47 @@ automatically receive statistics update.
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)
@ -462,7 +506,7 @@ extern "C" {
}
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;
blocks.reserve(total_blocks_number);
@ -492,6 +536,8 @@ extern "C" {
root.thread_name = name.data();
}
CsStatsMap per_thread_statistics_cs;
uint32_t blocks_number_in_thread = 0;
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
auto threshold = read_number + blocks_number_in_thread;
@ -534,6 +580,12 @@ extern "C" {
root.wait_time += baseData->duration();
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);
@ -547,6 +599,8 @@ extern "C" {
if (inFile.eof())
break;
StatsMap per_thread_statistics;
blocks_number_in_thread = 0;
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
threshold = read_number + blocks_number_in_thread;
@ -600,9 +654,6 @@ extern "C" {
tree.node = baseData;
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 block has runtime name then generate new id for such block.
@ -649,6 +700,7 @@ extern "C" {
if (gather_statistics)
{
EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta);
auto& per_parent_statistics = parent_statistics[thread_id];
per_parent_statistics.clear();
//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();
//});
::profiler::block_index_t cs_index = 0;
for (auto i : root.children)
{
auto& frame = blocks[i];
@ -739,6 +792,22 @@ extern "C" {
per_frame_statistics.clear();
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)
root.depth = frame.depth;

View File

@ -1501,27 +1501,69 @@ void EasyGraphicsView::onIdleTimeout()
auto cse = item->intersectEvent(pos);
if (cse)
{
const auto& itemBlock = cse->tree;
auto widget = new QWidget();
auto lay = new QGridLayout(widget);
int row = 0;
lay->addWidget(new EasyBoldLabel("Context switch event", widget), row, 0, 1, 2, Qt::AlignHCenter);
++row;
lay->addWidget(new QLabel("Thread:", widget), row, 0, Qt::AlignRight);
auto it = EASY_GLOBALS.profiler_blocks.find(cse->tree.node->id());
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);
else
lay->addWidget(new QLabel(QString::number(cse->tree.node->id()), widget), row, 1, Qt::AlignLeft);
++row;
lay->addWidget(new QLabel("Process:", widget), row, 0, Qt::AlignRight);
lay->addWidget(new QLabel(cse->tree.node->name(), widget), row, 1, Qt::AlignLeft);
++row;
const auto duration = itemBlock.node->duration();
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->setWidget(widget);