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:
parent
8cfeb1b7f2
commit
d85f9864d6
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user