Merge pull request #230 from rokopt/recursive-zone-timings

Introduce "non-reentrant time" statistics view
This commit is contained in:
Bartosz Taudul 2021-06-26 21:17:45 +02:00 committed by GitHub
commit 9f185310be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 281 additions and 77 deletions

View File

@ -2820,7 +2820,7 @@ If the trace capture was performed with call stack sampling enabled (as describe
Here you will find a multi-column display of captured zones, which contains: the zone \emph{name} and \emph{location}, \emph{total time} spent in the zone, the \emph{count} of zone executions and the \emph{mean time spent in the zone per call}. The view may be sorted according to the three displayed values.
The \emph{\faClock{}~Self time} option determines how the displayed time is calculated. If it is disabled, the measurements will be inclusive, that is, containing execution time of zone's children. Enabling the option switches the measurement to exclusive, displaying just the time spent in zone, subtracting the child calls.
In the \emph{~Accumulation mode} menu, the \emph{~Including children} selection selection displays inclusive measurements, that is, containing execution time of zone's children. The \emph{~Self only} selection switches the measurement to exclusive, displaying just the time spent in zone, subtracting the child calls. The \emph{~Non-reentrant} selection displays inclusive time, but counting only the first appearance of a given zone on a thread's stack.
Clicking the \LMB{} left mouse button on a zone will open the individual zone statistics view in the find zone window (section~\ref{findzone}).
@ -2847,7 +2847,7 @@ The \emph{Time} or \emph{Count} column (depending on the \emph{\faStopwatch{}~Sh
The last column, \emph{Code size}, displays the size of symbol in the executable image of the program. Since inlined routines are directly embedded into other functions, their symbol size will be based on the parent symbol, and displayed as 'less than'. In some cases this data won't be available. If the symbol code has been retrieved\footnote{Symbols larger than 64~KB are not captured.}, symbol size will be prepend with the \texttt{\faDatabase}~icon, and clicking the \RMB{}~right mouse button on the location column entry will open symbol view window (section~\ref{symbolview}).
Finally, the list can be filtered using the \emph{\faFilter{}~Filter symbols} entry field, just like in the instrumentation mode case. Additionally, you can also filter results by the originating image name of the symbol. Display of kernel symbols may be disabled with the \emph{\faHatWizard{}~Include kernel} switch. The exclusive/inclusive time counting mode can be switched using the \emph{\faClock{}~Self time} switch. Limiting the time range is also available, but is restricted to self time. If the \emph{\faPuzzlePiece{}~Show all} option is selected, the list will include not only call stack samples, but also all other symbols collected during the profiling process (this is enabled by default, if no sampling was performed).
Finally, the list can be filtered using the \emph{\faFilter{}~Filter symbols} entry field, just like in the instrumentation mode case. Additionally, you can also filter results by the originating image name of the symbol. Display of kernel symbols may be disabled with the \emph{\faHatWizard{}~Include kernel} switch. The exclusive/inclusive/non-reentrant time counting mode can be switched using the \emph{~Accumulation mode} menu. Limiting the time range is also available, but is restricted to self time. If the \emph{\faPuzzlePiece{}~Show all} option is selected, the list will include not only call stack samples, but also all other symbols collected during the profiling process (this is enabled by default, if no sampling was performed).
\subsection{Find zone window}
\label{findzone}

View File

@ -587,12 +587,57 @@ enum { GhostZoneSize = sizeof( GhostZone ) };
#pragma pack()
using SrcLocCountMap = unordered_flat_map<int16_t, size_t>;
static tracy_force_inline void IncSrcLocCount( SrcLocCountMap& countMap, int16_t srcloc )
{
const auto it = countMap.find( srcloc );
if( it == countMap.end() )
{
countMap.emplace( srcloc, 1 );
return;
}
assert( it->second != 0 );
it->second++;
}
static tracy_force_inline bool DecSrcLocCount( SrcLocCountMap& countMap, int16_t srcloc )
{
const auto it = countMap.find( srcloc );
assert( it != countMap.end() );
assert( it->second != 0 );
if( it->second == 1 )
{
countMap.erase( it );
return false;
}
it->second--;
return true;
}
static tracy_force_inline bool HasSrcLocCount( const SrcLocCountMap& countMap, int16_t srcloc )
{
const auto it = countMap.find( srcloc );
if( it != countMap.end() )
{
assert( it->second != 0 );
return true;
}
return false;
}
struct ThreadData
{
uint64_t id;
uint64_t count;
Vector<short_ptr<ZoneEvent>> timeline;
Vector<short_ptr<ZoneEvent>> stack;
SrcLocCountMap stackCount;
Vector<short_ptr<MessageData>> messages;
uint32_t nextZoneId;
Vector<uint32_t> zoneIdStack;
@ -604,6 +649,9 @@ struct ThreadData
Vector<SampleData> samples;
SampleData pendingSample;
uint64_t kernelSampleCnt;
tracy_force_inline void IncStackCount( int16_t srcloc ) { IncSrcLocCount( stackCount, srcloc ); }
tracy_force_inline bool DecStackCount( int16_t srcloc ) { return DecSrcLocCount( stackCount, srcloc ); }
};
struct GpuCtxThreadData

View File

@ -12269,9 +12269,23 @@ struct SrcLocZonesSlim
int16_t srcloc;
size_t numZones;
int64_t total;
int64_t selfTotal;
};
void View::AccumulationModeComboBox()
{
ImGui::TextUnformatted( "Timing" );
ImGui::SameLine();
const char* accumulationModeTable = m_statMode == 0 ? "Self only\0With children\0Non-reentrant\0" : "Self only\0With children\0";
ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Non-reentrant" ).x + ImGui::GetTextLineHeight() * 2 );
if ( m_statMode != 0 && m_statAccumulationMode == AccumulationMode::NonReentrantChildren )
{
m_statAccumulationMode = AccumulationMode::SelfOnly;
}
int accumulationMode = static_cast<int>( m_statAccumulationMode );
ImGui::Combo( "##accumulationMode", &accumulationMode, accumulationModeTable );
m_statAccumulationMode = static_cast<AccumulationMode>( accumulationMode );
}
void View::DrawStatistics()
{
ImGui::SetNextWindowSize( ImVec2( 1400, 600 ), ImGuiCond_FirstUseEver );
@ -12302,10 +12316,14 @@ void View::DrawStatistics()
if( hasSamples )
{
ImGui::Spacing();
ImGui::SameLine();
ImGui::RadioButton( ICON_FA_EYE_DROPPER " Sampling", &m_statMode, 1 );
}
else
{
ImGui::Spacing();
ImGui::SameLine();
ImGui::RadioButton( ICON_FA_PUZZLE_PIECE " Symbols", &m_statMode, 1 );
}
ImGui::SameLine();
@ -12349,19 +12367,18 @@ void View::DrawStatistics()
if( !filterActive )
{
auto cit = m_statCache.find( it->first );
if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.sourceCount == it->second.zones.size() )
if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
{
if( cit->second.count != 0 )
{
slzcnt++;
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total, cit->second.selfTotal } );
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
}
}
else
{
size_t cnt = 0;
int64_t total = 0;
int64_t selfTotal = 0;
for( auto& v : it->second.zones )
{
auto& z = *v.Zone();
@ -12370,17 +12387,24 @@ void View::DrawStatistics()
if( start >= min && end <= max )
{
const auto zt = end - start;
total += zt;
if( m_statSelf ) selfTotal += zt - GetZoneChildTimeFast( z );
if ( m_statAccumulationMode == AccumulationMode::SelfOnly)
{
total += zt - GetZoneChildTimeFast( z );
cnt++;
}
else if ( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry(z) )
{
total += zt;
cnt++;
}
}
}
if( cnt != 0 )
{
slzcnt++;
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total, selfTotal } );
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
}
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, it->second.zones.size(), cnt, total, selfTotal };
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
}
}
else
@ -12391,18 +12415,17 @@ void View::DrawStatistics()
if( m_statisticsFilter.PassFilter( name ) )
{
auto cit = m_statCache.find( it->first );
if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.sourceCount == it->second.zones.size() )
if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
{
if( cit->second.count != 0 )
{
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total, cit->second.selfTotal } );
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
}
}
else
{
size_t cnt = 0;
int64_t total = 0;
int64_t selfTotal = 0;
for( auto& v : it->second.zones )
{
auto& z = *v.Zone();
@ -12411,16 +12434,23 @@ void View::DrawStatistics()
if( start >= min && end <= max )
{
const auto zt = end - start;
total += zt;
if( m_statSelf ) selfTotal += zt - GetZoneChildTimeFast( z );
if ( m_statAccumulationMode == AccumulationMode::SelfOnly)
{
total += zt - GetZoneChildTimeFast( z );
cnt++;
}
else if ( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry(z) )
{
total += zt;
cnt++;
}
}
}
if( cnt != 0 )
{
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total, selfTotal } );
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
}
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, it->second.zones.size(), cnt, total, selfTotal };
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
}
}
}
@ -12434,9 +12464,26 @@ void View::DrawStatistics()
if( it->second.total != 0 )
{
slzcnt++;
size_t count;
int64_t total;
switch( m_statAccumulationMode )
{
case AccumulationMode::SelfOnly:
count = it->second.zones.size();
total = it->second.selfTotal;
break;
case AccumulationMode::AllChildren:
count = it->second.zones.size();
total = it->second.total;
break;
case AccumulationMode::NonReentrantChildren:
count = it->second.nonReentrantCount;
total = it->second.nonReentrantTotal;
break;
}
if( !filterActive )
{
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, it->second.zones.size(), it->second.total, it->second.selfTotal } );
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
}
else
{
@ -12444,7 +12491,7 @@ void View::DrawStatistics()
auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
if( m_statisticsFilter.PassFilter( name ) )
{
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, it->second.zones.size(), it->second.total, it->second.selfTotal } );
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
}
}
}
@ -12459,7 +12506,7 @@ void View::DrawStatistics()
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_CLOCK " Self time", &m_statSelf );
AccumulationModeComboBox();
}
else
{
@ -12469,7 +12516,7 @@ void View::DrawStatistics()
{
ImGui::PushItemFlag( ImGuiItemFlags_Disabled, true );
ImGui::PushStyleVar( ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f );
m_statSelf = true;
m_statAccumulationMode = AccumulationMode::SelfOnly;
bool val = true;
ImGui::Checkbox( ICON_FA_CLOCK " Self time", &val );
ImGui::PopItemFlag();
@ -12483,17 +12530,30 @@ void View::DrawStatistics()
}
else
{
ImGui::Checkbox( ICON_FA_CLOCK " Self time", &m_statSelf );
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
AccumulationModeComboBox();
}
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_EYE_SLASH " Hide unknown", &m_statHideUnknown );
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_PUZZLE_PIECE " Show all", &m_showAllSymbols );
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_SITEMAP " Inlines", &m_statSeparateInlines );
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_AT " Address", &m_statShowAddress );
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::TextUnformatted( "Location:" );
ImGui::SameLine();
const char* locationTable = "Entry\0Sample\0Smart\0";
@ -12614,19 +12674,6 @@ void View::DrawStatistics()
}
break;
case 2:
if( m_statSelf )
{
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal < rhs.selfTotal; } );
}
else
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal > rhs.selfTotal; } );
}
}
else
{
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total < rhs.total; } );
@ -12635,7 +12682,6 @@ void View::DrawStatistics()
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total > rhs.total; } );
}
}
break;
case 3:
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
@ -12648,19 +12694,6 @@ void View::DrawStatistics()
}
break;
case 4:
if( m_statSelf )
{
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal / lhs.numZones < rhs.selfTotal / rhs.numZones; } );
}
else
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal / lhs.numZones > rhs.selfTotal / rhs.numZones; } );
}
}
else
{
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones < rhs.total / rhs.numZones; } );
@ -12669,7 +12702,6 @@ void View::DrawStatistics()
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones > rhs.total / rhs.numZones; } );
}
}
break;
default:
assert( false );
@ -12721,7 +12753,7 @@ void View::DrawStatistics()
ImGui::Unindent( indentVal );
}
ImGui::TableNextColumn();
const auto time = m_statSelf ? v.selfTotal : v.total;
const auto time = v.total;
ImGui::TextUnformatted( TimeToString( time ) );
ImGui::SameLine();
char buf[64];
@ -12730,7 +12762,7 @@ void View::DrawStatistics()
ImGui::TableNextColumn();
ImGui::TextUnformatted( RealToString( v.numZones ) );
ImGui::TableNextColumn();
ImGui::TextUnformatted( TimeToString( ( m_statSelf ? v.selfTotal : v.total ) / v.numZones ) );
ImGui::TextUnformatted( TimeToString( time / v.numZones ) );
ImGui::PopID();
}
ImGui::EndTable();
@ -12971,7 +13003,7 @@ void View::DrawStatistics()
}
else
{
if( m_statSelf )
if( m_statAccumulationMode == AccumulationMode::SelfOnly )
{
pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l.excl != r.excl ? l.excl > r.excl : l.symAddr < r.symAddr; } );
}
@ -13008,7 +13040,7 @@ void View::DrawStatistics()
int idx = 0;
for( auto& v : data )
{
const auto cnt = m_statSelf ? v.excl : v.incl;
const auto cnt = m_statAccumulationMode == AccumulationMode::SelfOnly ? v.excl : v.incl;
if( cnt > 0 || showAll )
{
const char* name = "[unknown]";
@ -13247,7 +13279,7 @@ void View::DrawStatistics()
inSymList.push_back( SymList { v.symAddr, statIt->second.incl, statIt->second.excl } );
}
if( m_statSelf )
if( m_statAccumulationMode == AccumulationMode::SelfOnly )
{
pdqsort_branchless( inSymList.begin(), inSymList.end(), []( const auto& l, const auto& r ) { return l.excl != r.excl ? l.excl > r.excl : l.symAddr < r.symAddr; } );
}
@ -13261,7 +13293,7 @@ void View::DrawStatistics()
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
const auto cnt = m_statSelf ? iv.excl : iv.incl;
const auto cnt = m_statAccumulationMode == AccumulationMode::SelfOnly ? iv.excl : iv.incl;
if( cnt > 0 || showAll )
{
auto sit = symMap.find( iv.symAddr );
@ -17523,6 +17555,93 @@ const ZoneEvent* View::GetZoneParent( const ZoneEvent& zone, uint64_t tid ) cons
return nullptr;
}
bool View::IsZoneReentry( const ZoneEvent& zone ) const
{
#ifndef TRACY_NO_STATISTICS
if( m_worker.AreSourceLocationZonesReady() )
{
auto& slz = m_worker.GetZonesForSourceLocation( zone.SrcLoc() );
if( !slz.zones.empty() )
{
auto it = std::lower_bound( slz.zones.begin(), slz.zones.end(), zone.Start(), [] ( const auto& lhs, const auto& rhs ) { return lhs.Zone()->Start() < rhs; } );
if( it != slz.zones.end() && it->Zone() == &zone )
{
return IsZoneReentry( zone, m_worker.DecompressThread( it->Thread() ) );
}
}
}
#endif
for( const auto& thread : m_worker.GetThreadData() )
{
const ZoneEvent* parent = nullptr;
const Vector<short_ptr<ZoneEvent>>* timeline = &thread->timeline;
if( timeline->empty() ) continue;
for(;;)
{
if( timeline->is_magic() )
{
auto vec = (Vector<ZoneEvent>*)timeline;
auto it = std::upper_bound( vec->begin(), vec->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r.Start(); } );
if( it != vec->begin() ) --it;
if( zone.IsEndValid() && it->Start() > zone.End() ) break;
if( it == &zone ) return false;
if( !it->HasChildren() ) break;
parent = it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
else
{
auto it = std::upper_bound( timeline->begin(), timeline->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r->Start(); } );
if( it != timeline->begin() ) --it;
if( zone.IsEndValid() && (*it)->Start() > zone.End() ) break;
if( *it == &zone ) return false;
if( !(*it)->HasChildren() ) break;
parent = *it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
}
}
return false;
}
bool View::IsZoneReentry( const ZoneEvent& zone, uint64_t tid ) const
{
const auto thread = m_worker.GetThreadData( tid );
const ZoneEvent* parent = nullptr;
const Vector<short_ptr<ZoneEvent>>* timeline = &thread->timeline;
if( timeline->empty() ) return false;
for(;;)
{
if( timeline->is_magic() )
{
auto vec = (Vector<ZoneEvent>*)timeline;
auto it = std::upper_bound( vec->begin(), vec->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r.Start(); } );
if( it != vec->begin() ) --it;
if( zone.IsEndValid() && it->Start() > zone.End() ) break;
if( it == &zone ) return false;
if( !it->HasChildren() ) break;
parent = it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
else
{
auto it = std::upper_bound( timeline->begin(), timeline->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r->Start(); } );
if( it != timeline->begin() ) --it;
if( zone.IsEndValid() && (*it)->Start() > zone.End() ) break;
if( *it == &zone ) return false;
if( !(*it)->HasChildren() ) break;
parent = *it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
}
return false;
}
const GpuEvent* View::GetZoneParent( const GpuEvent& zone ) const
{
for( const auto& ctx : m_worker.GetGpuData() )

View File

@ -56,13 +56,20 @@ class View
uint64_t count;
};
enum class AccumulationMode
{
SelfOnly,
AllChildren,
NonReentrantChildren
};
struct StatisticsCache
{
RangeSlim range;
AccumulationMode accumulationMode;
size_t sourceCount;
size_t count;
int64_t total;
int64_t selfTotal;
};
public:
@ -187,6 +194,7 @@ private:
void DrawMessages();
void DrawMessageLine( const MessageData& msg, bool hasCallstack, int& idx );
void DrawFindZone();
void AccumulationModeComboBox();
void DrawStatistics();
void DrawMemory();
void DrawAllocList();
@ -250,6 +258,8 @@ private:
const ZoneEvent* GetZoneParent( const ZoneEvent& zone ) const;
const ZoneEvent* GetZoneParent( const ZoneEvent& zone, uint64_t tid ) const;
bool IsZoneReentry( const ZoneEvent& zone ) const;
bool IsZoneReentry( const ZoneEvent& zone, uint64_t tid ) const;
const GpuEvent* GetZoneParent( const GpuEvent& zone ) const;
const ThreadData* GetZoneThreadData( const ZoneEvent& zone ) const;
uint64_t GetZoneThread( const ZoneEvent& zone ) const;
@ -392,7 +402,7 @@ private:
bool m_showCpuDataWindow = false;
bool m_showAnnotationList = false;
bool m_statSelf = true;
AccumulationMode m_statAccumulationMode = AccumulationMode::SelfOnly;
bool m_statSampleTime = true;
int m_statMode = 0;
int m_statSampleLocation = 2;

View File

@ -389,11 +389,11 @@ Worker::Worker( const char* name, const char* program, const std::vector<ImportE
else
{
auto td = NoticeThread( v.tid );
if (td->zoneIdStack.empty())
continue;
if( td->zoneIdStack.empty() ) continue;
td->zoneIdStack.pop_back();
auto& stack = td->stack;
auto zone = stack.back_and_pop();
td->DecStackCount( zone->SrcLoc() );
zone->SetEnd( v.timestamp );
#ifndef TRACY_NO_STATISTICS
@ -564,8 +564,7 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
char tmp[1024];
f.Read( tmp, sz );
m_captureName = std::string( tmp, tmp+sz );
if (m_captureName.empty())
m_captureName = f.GetFilename();
if( m_captureName.empty() ) m_captureName = f.GetFilename();
}
{
f.Read( sz );
@ -1819,16 +1818,21 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
if( mem.second->reconstruct ) jobs.emplace_back( std::thread( [this, mem = mem.second] { ReconstructMemAllocPlot( *mem ); } ) );
}
std::function<void(Vector<short_ptr<ZoneEvent>>&, uint16_t)> ProcessTimeline;
ProcessTimeline = [this, &ProcessTimeline] ( Vector<short_ptr<ZoneEvent>>& _vec, uint16_t thread )
std::function<void(SrcLocCountMap&, Vector<short_ptr<ZoneEvent>>&, uint16_t)> ProcessTimeline;
ProcessTimeline = [this, &ProcessTimeline] ( SrcLocCountMap& countMap, Vector<short_ptr<ZoneEvent>>& _vec, uint16_t thread )
{
if( m_shutdown.load( std::memory_order_relaxed ) ) return;
assert( _vec.is_magic() );
auto& vec = *(Vector<ZoneEvent>*)( &_vec );
for( auto& zone : vec )
{
if( zone.IsEndValid() ) ReconstructZoneStatistics( zone, thread );
if( zone.HasChildren() ) ProcessTimeline( GetZoneChildrenMutable( zone.Child() ), thread );
if( zone.IsEndValid() ) ReconstructZoneStatistics( countMap, zone, thread );
if( zone.HasChildren() )
{
IncSrcLocCount( countMap, zone.SrcLoc() );
ProcessTimeline( countMap, GetZoneChildrenMutable( zone.Child() ), thread );
DecSrcLocCount( countMap, zone.SrcLoc() );
}
}
};
@ -1838,8 +1842,9 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
if( m_shutdown.load( std::memory_order_relaxed ) ) return;
if( !t->timeline.empty() )
{
SrcLocCountMap countMap;
// Don't touch thread compression cache in a thread.
ProcessTimeline( t->timeline, m_data.localThreadCompress.DecompressMustRaw( t->id ) );
ProcessTimeline( countMap, t->timeline, m_data.localThreadCompress.DecompressMustRaw( t->id ) );
}
}
} ) );
@ -2006,6 +2011,7 @@ Worker::~Worker()
{
v->timeline.~Vector();
v->stack.~Vector();
v->stackCount.~Table();
v->messages.~Vector();
v->zoneIdStack.~Vector();
v->samples.~Vector();
@ -3581,6 +3587,7 @@ void Worker::NewZone( ZoneEvent* zone, uint64_t thread )
auto td = m_threadCtxData;
if( !td ) td = m_threadCtxData = NoticeThread( thread );
td->count++;
td->IncStackCount( zone->SrcLoc() );
const auto ssz = td->stack.size();
if( ssz == 0 )
{
@ -4735,6 +4742,7 @@ void Worker::ProcessZoneEnd( const QueueZoneEnd& ev )
assert( !stack.empty() );
auto zone = stack.back_and_pop();
assert( zone->End() == -1 );
const auto isReentry = td->DecStackCount( zone->SrcLoc() );
const auto refTime = m_refTimeThread + ev.time;
m_refTimeThread = refTime;
const auto timeEnd = TscTime( refTime - m_data.baseTime );
@ -4788,6 +4796,13 @@ void Worker::ProcessZoneEnd( const QueueZoneEnd& ev )
if( slz->selfMin > selfSpan ) slz->selfMin = selfSpan;
if( slz->selfMax < selfSpan ) slz->selfMax = selfSpan;
slz->selfTotal += selfSpan;
if( !isReentry )
{
slz->nonReentrantCount++;
if( slz->nonReentrantMin > timeSpan ) slz->nonReentrantMin = timeSpan;
if( slz->nonReentrantMax < timeSpan ) slz->nonReentrantMax = timeSpan;
slz->nonReentrantTotal += timeSpan;
}
if( !td->childTimeStack.empty() )
{
td->childTimeStack.back() += timeSpan;
@ -7041,7 +7056,7 @@ void Worker::ReadTimelineHaveSize( FileRead& f, GpuEvent* zone, int64_t& refTime
}
#ifndef TRACY_NO_STATISTICS
void Worker::ReconstructZoneStatistics( ZoneEvent& zone, uint16_t thread )
void Worker::ReconstructZoneStatistics( SrcLocCountMap& countMap, ZoneEvent& zone, uint16_t thread )
{
assert( zone.IsEndValid() );
auto timeSpan = zone.End() - zone.Start();
@ -7058,6 +7073,14 @@ void Worker::ReconstructZoneStatistics( ZoneEvent& zone, uint16_t thread )
if( slz.max < timeSpan ) slz.max = timeSpan;
slz.total += timeSpan;
slz.sumSq += double( timeSpan ) * timeSpan;
const auto isReentry = HasSrcLocCount( countMap, zone.SrcLoc() );
if( !isReentry )
{
slz.nonReentrantCount++;
if( slz.nonReentrantMin > timeSpan ) slz.nonReentrantMin = timeSpan;
if( slz.nonReentrantMax < timeSpan ) slz.nonReentrantMax = timeSpan;
slz.nonReentrantTotal += timeSpan;
}
if( zone.HasChildren() )
{
auto& children = GetZoneChildren( zone.Child() );

View File

@ -184,6 +184,10 @@ private:
int64_t selfMin = std::numeric_limits<int64_t>::max();
int64_t selfMax = std::numeric_limits<int64_t>::min();
int64_t selfTotal = 0;
size_t nonReentrantCount = 0;
int64_t nonReentrantMin = std::numeric_limits<int64_t>::max();
int64_t nonReentrantMax = std::numeric_limits<int64_t>::min();
int64_t nonReentrantTotal = 0;
};
struct CallstackFrameIdHash
@ -831,7 +835,7 @@ private:
tracy_force_inline void ReadTimelineHaveSize( FileRead& f, GpuEvent* zone, int64_t& refTime, int64_t& refGpuTime, int32_t& childIdx, uint64_t sz );
#ifndef TRACY_NO_STATISTICS
tracy_force_inline void ReconstructZoneStatistics( ZoneEvent& zone, uint16_t thread );
tracy_force_inline void ReconstructZoneStatistics( SrcLocCountMap& countMap, ZoneEvent& zone, uint16_t thread );
#else
tracy_force_inline void CountZoneStatistics( ZoneEvent* zone );
#endif