Merge pull request #1469 from wolfpld/slomp/gpu-stats

GPU Zone Statistics
This commit is contained in:
Bartosz Taudul
2026-09-11 20:42:41 +02:00
committed by GitHub
9 changed files with 925 additions and 726 deletions
+5
View File
@@ -2,6 +2,11 @@ Note: There is no guarantee that version mismatched client and server will
be able to talk with each other. Network protocol breakages won't be listed
here.
vx.xx.x (2026-xx-xx)
--------------------
- Find zone window now works with GPU zones (histogram, statistics, self time).
v0.14.1 (2026-08-22)
--------------------
+2
View File
@@ -4474,6 +4474,8 @@ Tracy gives you the ability to display an execution time histogram of all occurr
You start by entering a search query, which will be matched against known zone names (see section~\ref{markingzones} for information on the grouping of zone names). If the search found some results, you will be presented with a list of zones in the \emph{matched source locations} drop-down. The selected zone's graph is displayed on the \emph{histogram} drop-down, and also the matching zones are highlighted on the timeline view.
GPU zones are supported as well, using their GPU execution time. Zone grouping, the found-zones list, and call stack samples are available only for CPU zones.
Clicking the \RMB{}~right mouse button on the source file location will open the source file view window (if applicable, see section~\ref{sourceview}). If symbol data is available Tracy will try to match the instrumented zone name to a captured symbol. If this succeeds and there are no duplicate matches, the source file view will be accompanied by the disassembly of the code. Since this matching is not exact, in rare cases you may get the wrong data here. To just display the source code, press and hold the \keys{\ctrl} key while clicking the \RMB{}~right mouse button.
An example histogram is presented in figure~\ref{findzonehistogram}. Here you can see that the majority of zone calls (by count) are clustered in the 300~\si{\nano\second} group, closely followed by the 10~\si{\micro\second} cluster. There are some outliers at the 1~and~10~\si{\milli\second} marks, which can be ignored on most occasions, as these are single occurrences.
+2
View File
@@ -331,6 +331,7 @@ private:
void DrawMessages();
void DrawMessageLine( const MessageData& msg, bool hasCallstack, int& idx );
void DrawFindZone();
void DrawFindZoneCpuDetails( int16_t matchSrcloc );
void AccumulationModeComboBox();
void DrawStatistics();
void DrawSamplesStatistics(Vector<SymList>& data, int64_t timeRange, uint64_t totalSamples, AccumulationMode accumulationMode);
@@ -440,6 +441,7 @@ private:
void FindZones();
void FindZonesCompare();
bool IsGpuSourceLocation( int16_t srcloc ) const;
std::vector<MemoryPage> GetMemoryPages() const;
File diff suppressed because it is too large Load Diff
@@ -721,7 +721,7 @@ void View::DrawStatistics()
auto name = m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function );
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
ImGui::SameLine();
if( m_statMode == 0 )
if( m_statMode == 0 || m_statMode == 2 )
{
if( ImGui::Selectable( name, m_findZone.show && !m_findZone.match.empty() && m_findZone.match[m_findZone.selMatch] == v.srcloc, ImGuiSelectableFlags_SpanAllColumns ) )
{
+1 -23
View File
@@ -577,29 +577,7 @@ int64_t View::GetZoneChildTime( const ZoneEvent& zone )
int64_t View::GetZoneChildTime( const GpuEvent& zone )
{
int64_t time = 0;
if( zone.Child() >= 0 )
{
auto& children = m_worker.GetGpuChildren( zone.Child() );
if( children.is_magic() )
{
auto& vec = *(Vector<GpuEvent>*)&children;
for( auto& v : vec )
{
const auto childSpan = std::max( int64_t( 0 ), v.GpuEnd() - v.GpuStart() );
time += childSpan;
}
}
else
{
for( auto& v : children )
{
const auto childSpan = std::max( int64_t( 0 ), v->GpuEnd() - v->GpuStart() );
time += childSpan;
}
}
}
return time;
return m_worker.GetGpuChildTime( zone );
}
int64_t View::GetZoneChildTimeFast( const ZoneEvent& zone )
@@ -1489,6 +1489,19 @@ void View::DrawGpuInfoWindow()
ShowZoneInfo( *parent, m_gpuInfoWindowThread );
}
}
if( m_worker.AreGpuSourceLocationZonesReady() )
{
const auto sl = ev.SrcLoc();
const auto& slz = m_worker.GetGpuZonesForSourceLocation( sl );
if( !slz.zones.empty() )
{
ImGui::SameLine();
if( ImGui::Button( ICON_FA_CHART_BAR " Statistics" ) )
{
m_findZone.ShowZone( sl, m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ) );
}
}
}
if( ev.callstack.Val() != 0 )
{
ImGui::SameLine();
+51
View File
@@ -2718,6 +2718,22 @@ const Worker::SourceLocationZones& Worker::GetZonesForSourceLocation( int16_t sr
return it != m_data.sourceLocationZones.end() ? it->second : empty;
}
Worker::GpuSourceLocationZones& Worker::GetGpuZonesForSourceLocation( int16_t srcloc )
{
assert( AreGpuSourceLocationZonesReady() );
static GpuSourceLocationZones empty;
auto it = m_data.gpuSourceLocationZones.find( srcloc );
return it != m_data.gpuSourceLocationZones.end() ? it->second : empty;
}
const Worker::GpuSourceLocationZones& Worker::GetGpuZonesForSourceLocation( int16_t srcloc ) const
{
assert( AreGpuSourceLocationZonesReady() );
static const GpuSourceLocationZones empty;
auto it = m_data.gpuSourceLocationZones.find( srcloc );
return it != m_data.gpuSourceLocationZones.end() ? it->second : empty;
}
const SymbolStats* Worker::GetSymbolStats( uint64_t symAddr ) const
{
assert( AreCallstackSamplesReady() );
@@ -3582,6 +3598,32 @@ ThreadData* Worker::GetCurrentThreadData()
return td;
}
int64_t Worker::GetGpuChildTime( const GpuEvent& zone ) const
{
// Unresolved children (GpuEnd == -1, e.g. aborted trace) clamp to 0, overstating parent self-time.
int64_t time = 0;
if( zone.Child() >= 0 )
{
auto& children = GetGpuChildren( zone.Child() );
if( children.is_magic() )
{
auto& vec = *(Vector<GpuEvent>*)&children;
for( auto& v : vec )
{
time += std::max( int64_t( 0 ), v.GpuEnd() - v.GpuStart() );
}
}
else
{
for( auto& v : children )
{
time += std::max( int64_t( 0 ), v->GpuEnd() - v->GpuStart() );
}
}
}
return time;
}
#ifndef TRACY_NO_STATISTICS
Worker::SourceLocationZones* Worker::GetSourceLocationZonesReal( uint16_t srcloc )
{
@@ -3603,6 +3645,7 @@ Worker::GpuSourceLocationZones* Worker::GetGpuSourceLocationZonesReal( uint16_t
m_data.gpuZonesLast.second = &it->second;
return &it->second;
}
#else
uint64_t* Worker::GetSourceLocationZonesCntReal( uint16_t srcloc )
{
@@ -6200,6 +6243,10 @@ void Worker::ProcessGpuTime( const QueueGpuTime& ev )
if( slz->max < timeSpan ) slz->max = timeSpan;
slz->total += timeSpan;
slz->sumSq += double( timeSpan ) * timeSpan;
const auto selfSpan = timeSpan - GetGpuChildTime( *zone );
if( slz->selfMin > selfSpan ) slz->selfMin = selfSpan;
if( slz->selfMax < selfSpan ) slz->selfMax = selfSpan;
slz->selfTotal += selfSpan;
}
#else
CountZoneStatistics( zone );
@@ -8118,6 +8165,10 @@ void Worker::ReconstructZoneStatistics( GpuEvent& zone, uint16_t thread )
if( slz.max < timeSpan ) slz.max = timeSpan;
slz.total += timeSpan;
slz.sumSq += double( timeSpan ) * timeSpan;
const auto selfSpan = timeSpan - GetGpuChildTime( zone );
if( slz.selfMin > selfSpan ) slz.selfMin = selfSpan;
if( slz.selfMax < selfSpan ) slz.selfMax = selfSpan;
slz.selfTotal += selfSpan;
}
}
#else
+6
View File
@@ -224,6 +224,9 @@ private:
int64_t max = std::numeric_limits<int64_t>::min();
int64_t total = 0;
double sumSq = 0;
int64_t selfMin = std::numeric_limits<int64_t>::max();
int64_t selfMax = std::numeric_limits<int64_t>::min();
int64_t selfTotal = 0;
};
struct CallstackFrameIdHash
@@ -630,6 +633,7 @@ public:
tracy_force_inline const Vector<short_ptr<ZoneEvent>>& GetZoneChildren( int32_t idx ) const { return m_data.zoneChildren[idx]; }
tracy_force_inline const Vector<short_ptr<GpuEvent>>& GetGpuChildren( int32_t idx ) const { return m_data.gpuChildren[idx]; }
int64_t GetGpuChildTime( const GpuEvent& zone ) const;
#ifndef TRACY_NO_STATISTICS
tracy_force_inline const Vector<GhostZone>& GetGhostChildren( int32_t idx ) const { return m_data.ghostChildren[idx]; }
tracy_force_inline const GhostKey& GetGhostFrame( const Int24& frame ) const { return m_data.ghostFrames[frame.Val()]; }
@@ -645,6 +649,8 @@ public:
#ifndef TRACY_NO_STATISTICS
SourceLocationZones& GetZonesForSourceLocation( int16_t srcloc );
const SourceLocationZones& GetZonesForSourceLocation( int16_t srcloc ) const;
GpuSourceLocationZones& GetGpuZonesForSourceLocation( int16_t srcloc );
const GpuSourceLocationZones& GetGpuZonesForSourceLocation( int16_t srcloc ) const;
const unordered_flat_map<int16_t, SourceLocationZones>& GetSourceLocationZones() const { return m_data.sourceLocationZones; }
const unordered_flat_map<int16_t, GpuSourceLocationZones>& GetGpuSourceLocationZones() const { return m_data.gpuSourceLocationZones; }
bool AreSourceLocationZonesReady() const { return m_data.sourceLocationZonesReady; }