Apply group filters to the samples statistics in the "find zone" window

reduce memory by storing the thread ids next to the zones intead of
making zone lists per thread.

speed-up by reading the zones in linear order rather than bisecting
the whole list for each sample.
This commit is contained in:
xavier 2021-09-13 21:40:02 +02:00
parent 41130d2a69
commit 63acfe72e7
2 changed files with 105 additions and 66 deletions

View File

@ -10721,42 +10721,41 @@ void View::DrawFindZone()
ImGui::SameLine(); ImGui::SameLine();
if( ImGui::Checkbox( ICON_FA_HAT_WIZARD " Include kernel", &m_statShowKernel )) if( ImGui::Checkbox( ICON_FA_HAT_WIZARD " Include kernel", &m_statShowKernel ))
{ {
m_findZone.samplesCache.scheduleUpdate = true; m_findZone.samples.scheduleUpdate = true;
} }
} }
if( !m_findZone.samplesCache.needZonesPerThread ) if( !m_findZone.samples.enabled )
{ {
m_findZone.samplesCache.needZonesPerThread = true; m_findZone.samples.enabled = true;
m_findZone.samples.scheduleUpdate = true;
m_findZone.scheduleResetMatch = true; m_findZone.scheduleResetMatch = true;
} }
if( m_findZone.samplesCache.scheduleUpdate && !m_findZone.scheduleResetMatch ) if( m_findZone.samples.enabled && m_findZone.samples.scheduleUpdate && !m_findZone.scheduleResetMatch )
{ {
m_findZone.samplesCache.scheduleUpdate = false; m_findZone.samples.scheduleUpdate = false;
m_findZone.samplesCache.timeRange = 0;
for( auto& t: m_findZone.samplesCache.threads )
{
pdqsort_branchless( t.second.begin(), t.second.end(), []( const auto& lhs, const auto& rhs ) { return (*lhs).Start() < (*rhs).Start(); } );
int64_t prevEnd = 0;
for( const auto& it : t.second )
{
int64_t start = (*it).Start();
int64_t end = (*it).End();
if( start < prevEnd ) start = prevEnd;
if( start < end )
{
prevEnd = end;
m_findZone.samplesCache.timeRange += end - start;
}
}
}
const auto& symMap = m_worker.GetSymbolMap(); const auto& symMap = m_worker.GetSymbolMap();
m_findZone.samplesCache.counts.clear(); m_findZone.samples.counts.clear();
m_findZone.samplesCache.counts.reserve( symMap.size() ); m_findZone.samples.counts.reserve( symMap.size() );
struct GroupRange {
const FindZone::Group* group;
Vector<short_ptr<ZoneEvent>>::const_iterator begin;
Vector<short_ptr<ZoneEvent>>::const_iterator end;
};
Vector<GroupRange> selectedGroups;
selectedGroups.reserve( m_findZone.groups.size() );
for( auto it = m_findZone.groups.begin(); it != m_findZone.groups.end(); ++it )
{
assert( it->second.zones.size() == it->second.zonesTids.size() );
if( ( m_findZone.selGroup == m_findZone.Unselected || it->first == m_findZone.selGroup )
&& !it->second.zones.empty() )
{
selectedGroups.push_back_no_space_check( GroupRange{&it->second} );
}
}
for( auto& v : symMap ) for( auto& v : symMap )
{ {
@ -10777,37 +10776,81 @@ void View::DrawFindZone()
auto samples = m_worker.GetSamplesForSymbol( v.first ); auto samples = m_worker.GetSamplesForSymbol( v.first );
if( !samples ) continue; if( !samples ) continue;
if( samples->empty() ) continue;
auto samplesBegin = samples->begin();
auto samplesEnd = samples->end();
if( m_findZone.range.active )
{
const auto rangeMin = m_findZone.range.min;
const auto rangeMax = m_findZone.range.max;
samplesBegin = std::lower_bound( samplesBegin, samplesEnd, rangeMin, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
if( samplesBegin != samplesEnd )
{
samplesEnd = std::lower_bound( samplesBegin, samplesEnd, rangeMax, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
}
}
if( samplesBegin == samplesEnd ) continue;
bool empty = true;
const auto firstTime = samplesBegin->time.Val();
const auto lastTime = samplesEnd->time.Val();
for( auto& g: selectedGroups )
{
const auto& zones = g.group->zones;
auto begin = std::lower_bound( zones.begin(), zones.end(), firstTime, [] ( const auto& l, const auto& r ) { return l->Start() < r; } );
auto end = std::upper_bound( begin, zones.end(), lastTime, [] ( const auto& l, const auto& r ) { return l <= r->Start(); } );
g.begin = begin;
g.end = end;
empty = empty && (begin == end);
}
if (empty) continue;
uint32_t count = 0; uint32_t count = 0;
for( auto it: *samples ) for( auto it = samplesBegin; it != samplesEnd; ++it )
{ {
const auto time = it.time.Val(); const auto time = it->time.Val();
const auto& zones = m_findZone.samplesCache.threads[it.thread]; bool pass = false;
auto z = std::lower_bound( zones.begin(), zones.end(), time, [] ( const auto& l, const auto& r ) { return l->Start() < r; } ); for( auto& g: selectedGroups )
if( z == zones.end() ) continue; {
if( z != zones.begin() ) --z; while( g.begin != g.end && time > (*g.begin)->End() ) ++g.begin;
if( time >= (*z)->Start() && time < (*z)->End() ) if( g.begin == g.end ) continue;
count++; if( time < (*g.begin)->Start() ) continue;
const auto& tids = g.group->zonesTids;
const auto firstZone = g.group->zones.begin();
for (auto z = g.begin; z != g.end && (*z)->Start() <= time; ++z)
{
auto zoneIndex = z - firstZone;
if( (*z)->End() > time && it->thread == tids[zoneIndex] )
{
pass = true;
break;
}
}
}
if( pass ) count ++;
} }
if( count > 0 ) m_findZone.samplesCache.counts.push_back_no_space_check( SymList { v.first, 0, count } ); if( count > 0 ) m_findZone.samples.counts.push_back_no_space_check( SymList { v.first, 0, count } );
} }
} }
Vector<SymList> data; Vector<SymList> data;
data.reserve( m_findZone.samplesCache.counts.size() ); data.reserve( m_findZone.samples.counts.size() );
for( auto it: m_findZone.samplesCache.counts ) data.push_back_no_space_check( it ); for( auto it: m_findZone.samples.counts ) data.push_back_no_space_check( it );
DrawSamplesStatistics( data, m_findZone.samplesCache.timeRange, AccumulationMode::SelfOnly ); int64_t timeRange = ( m_findZone.selGroup != m_findZone.Unselected ) ? m_findZone.selTotal : m_findZone.total;
DrawSamplesStatistics( data, timeRange, AccumulationMode::SelfOnly );
ImGui::TreePop(); ImGui::TreePop();
} }
else else
{ {
if( m_findZone.samplesCache.needZonesPerThread ) if( m_findZone.samples.enabled )
{ {
m_findZone.samplesCache.needZonesPerThread = false; m_findZone.samples.enabled = false;
m_findZone.samplesCache.scheduleUpdate = false; m_findZone.samples.scheduleUpdate = false;
m_findZone.samplesCache.counts = Vector<SymList>(); m_findZone.samples.counts = Vector<SymList>();
m_findZone.samplesCache.threads = unordered_flat_map<uint16_t, Vector<short_ptr<ZoneEvent>> >(); for( auto& it: m_findZone.groups ) it.second.zonesTids.clear();
} }
} }
ImGui::Separator(); ImGui::Separator();
@ -10866,9 +10909,8 @@ void View::DrawFindZone()
const auto highlightActive = m_findZone.highlight.active; const auto highlightActive = m_findZone.highlight.active;
const auto limitRange = m_findZone.range.active; const auto limitRange = m_findZone.range.active;
FindZone::Group* group = nullptr; FindZone::Group* group = nullptr;
uint64_t lastGid = std::numeric_limits<uint64_t>::max() - 1; const uint64_t invalidGid = std::numeric_limits<uint64_t>::max() - 1;
Vector<short_ptr<ZoneEvent>>* threadZones = nullptr; uint64_t lastGid = invalidGid;
uint16_t lastTid = std::numeric_limits<uint16_t>::max();
auto zptr = zones.data() + m_findZone.processed; auto zptr = zones.data() + m_findZone.processed;
const auto zend = zones.data() + zones.size(); const auto zend = zones.data() + zones.size();
while( zptr < zend ) while( zptr < zend )
@ -10964,27 +11006,25 @@ void View::DrawFindZone()
{ {
it = m_findZone.groups.emplace( gid, FindZone::Group { m_findZone.groupId++ } ).first; it = m_findZone.groups.emplace( gid, FindZone::Group { m_findZone.groupId++ } ).first;
it->second.zones.reserve( 1024 ); it->second.zones.reserve( 1024 );
if( m_findZone.samples.enabled )
it->second.zonesTids.reserve( 1024 );
} }
group = &it->second; group = &it->second;
} }
group->time += timespan; group->time += timespan;
group->zones.push_back_non_empty( ev.Zone() ); group->zones.push_back_non_empty( ev.Zone() );
if( m_findZone.samples.enabled )
group->zonesTids.push_back_non_empty( ev.Thread() );
if( m_findZone.samplesCache.needZonesPerThread )
{
if( lastTid != ev.Thread() )
{
lastTid = ev.Thread();
threadZones = &m_findZone.samplesCache.threads[lastTid];
threadZones->reserve( 1024 );
}
threadZones->push_back_non_empty( ev.Zone() );
m_findZone.samplesCache.scheduleUpdate = true;
}
} }
m_findZone.processed = zptr - zones.data(); m_findZone.processed = zptr - zones.data();
const bool groupsUpdated = lastGid != invalidGid;
if( m_findZone.samples.enabled && groupsUpdated )
{
m_findZone.samples.scheduleUpdate = true;
}
Vector<decltype( m_findZone.groups )::iterator> groups; Vector<decltype( m_findZone.groups )::iterator> groups;
groups.reserve_and_use( m_findZone.groups.size() ); groups.reserve_and_use( m_findZone.groups.size() );
int idx = 0; int idx = 0;

View File

@ -516,6 +516,7 @@ private:
{ {
uint16_t id; uint16_t id;
Vector<short_ptr<ZoneEvent>> zones; Vector<short_ptr<ZoneEvent>> zones;
Vector<uint16_t> zonesTids;
int64_t time = 0; int64_t time = 0;
}; };
@ -563,12 +564,10 @@ private:
} binCache; } binCache;
struct { struct {
unordered_flat_map<uint16_t, Vector<short_ptr<ZoneEvent>> > threads;
Vector<SymList> counts; Vector<SymList> counts;
int64_t timeRange = 0;
bool scheduleUpdate = false; bool scheduleUpdate = false;
bool needZonesPerThread = false; bool enabled = false;
} samplesCache; } samples;
void Reset() void Reset()
{ {
@ -595,8 +594,6 @@ private:
{ {
ResetSelection(); ResetSelection();
groups.clear(); groups.clear();
samplesCache.threads.clear();
samplesCache.counts.clear();
processed = 0; processed = 0;
groupId = 0; groupId = 0;
selCs = 0; selCs = 0;
@ -613,6 +610,8 @@ private:
selTotal = 0; selTotal = 0;
selTime = 0; selTime = 0;
binCache.numBins = -1; binCache.numBins = -1;
samples.counts.clear();
samples.scheduleUpdate = true;
} }
void ShowZone( int16_t srcloc, const char* name ) void ShowZone( int16_t srcloc, const char* name )