GPU Zone Statistics

This commit is contained in:
Marcos Slomp
2026-09-09 10:18:59 -07:00
parent b892dd0b96
commit 44eabc5814
6 changed files with 280 additions and 87 deletions

View File

@@ -440,6 +440,7 @@ private:
void FindZones();
void FindZonesCompare();
bool IsGpuSourceLocation( int16_t srcloc ) const;
std::vector<MemoryPage> GetMemoryPages() const;

View File

@@ -23,10 +23,14 @@ void View::FindZones()
m_findZone.match = m_worker.GetMatchingSourceLocation( m_findZone.pattern, m_findZone.ignoreCase );
if( m_findZone.match.empty() ) return;
const auto& gpuMap = m_worker.GetGpuSourceLocationZones();
auto it = m_findZone.match.begin();
while( it != m_findZone.match.end() )
{
if( m_worker.GetZonesForSourceLocation( *it ).zones.empty() )
const bool hasCpu = !m_worker.GetZonesForSourceLocation( *it ).zones.empty();
const auto gpu_it = gpuMap.find( *it );
const bool hasGpu = gpu_it != gpuMap.end() && !gpu_it->second.zones.empty();
if( !hasCpu && !hasGpu )
{
it = m_findZone.match.erase( it );
}
@@ -37,6 +41,14 @@ void View::FindZones()
}
}
bool View::IsGpuSourceLocation( int16_t srcloc ) const
{
if( !m_worker.GetZonesForSourceLocation( srcloc ).zones.empty() ) return false;
const auto& gpuMap = m_worker.GetGpuSourceLocationZones();
const auto it = gpuMap.find( srcloc );
return it != gpuMap.end() && !it->second.zones.empty();
}
uint64_t View::GetSelectionTarget( const Worker::ZoneThreadData& ev, FindZone::GroupBy groupBy ) const
{
switch( groupBy )
@@ -372,7 +384,17 @@ void View::DrawFindZone()
for( auto& v : m_findZone.match )
{
auto& srcloc = m_worker.GetSourceLocation( v );
auto& zones = m_worker.GetZonesForSourceLocation( v ).zones;
const auto isGpuLoc = IsGpuSourceLocation( v );
size_t zoneCnt;
if( isGpuLoc )
{
const auto& gpuMap = m_worker.GetGpuSourceLocationZones();
zoneCnt = gpuMap.find( v )->second.zones.size();
}
else
{
zoneCnt = m_worker.GetZonesForSourceLocation( v ).zones.size();
}
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
ImGui::SameLine();
ImGui::PushID( idx );
@@ -390,7 +412,7 @@ void View::DrawFindZone()
ImGui::SameLine();
}
const auto fileName = m_worker.GetString( srcloc.file );
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s", RealToString( zones.size() ), LocationToString( fileName, srcloc.line ) );
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s%s", RealToString( zoneCnt ), isGpuLoc ? "[GPU] " : "", LocationToString( fileName, srcloc.line ) );
if( ImGui::IsItemHovered() )
{
DrawSourceTooltip( fileName, srcloc.line, srcloc.line );
@@ -423,9 +445,27 @@ void View::DrawFindZone()
ImGui::Separator();
auto& zoneData = m_worker.GetZonesForSourceLocation( m_findZone.match[m_findZone.selMatch] );
auto& zones = zoneData.zones;
zones.ensure_sorted();
const auto matchSrcloc = m_findZone.match[m_findZone.selMatch];
const bool isGpu = IsGpuSourceLocation( matchSrcloc );
size_t zsz;
int64_t rawTotal, selfTotal;
if( !isGpu )
{
auto& zoneData = m_worker.GetZonesForSourceLocation( matchSrcloc );
zoneData.zones.ensure_sorted();
zsz = zoneData.zones.size();
rawTotal = zoneData.total;
selfTotal = zoneData.selfTotal;
}
else
{
auto& zoneData = m_worker.GetGpuZonesForSourceLocation( matchSrcloc );
zoneData.zones.ensure_sorted();
zsz = zoneData.zones.size();
rawTotal = zoneData.total;
selfTotal = zoneData.selfTotal;
}
if( ImGui::TreeNodeEx( "Histogram", ImGuiTreeNodeFlags_DefaultOpen ) )
{
const auto ty = ImGui::GetTextLineHeight();
@@ -434,109 +474,180 @@ void View::DrawFindZone()
int64_t tmax = m_findZone.tmax;
int64_t total = m_findZone.total;
double sumSq = m_findZone.sumSq;
const auto zsz = zones.size();
if( m_findZone.sortedNum != zsz )
{
auto& vec = m_findZone.sorted;
const auto vszorig = vec.size();
vec.reserve( zsz );
size_t i;
if( m_findZone.runningTime )
if( !isGpu )
{
if( m_findZone.range.active )
auto& zoneData = m_worker.GetZonesForSourceLocation( matchSrcloc );
auto& zones = zoneData.zones;
if( m_findZone.runningTime )
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
if( m_findZone.range.active )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
if( end > rangeMax || zone.Start() < rangeMin ) continue;
const auto ctx = m_worker.GetContextSwitchData( m_worker.DecompressThread( zones[i].Thread() ) );
if( !ctx ) break;
int64_t t;
if( !GetZoneRunningTime( ctx, zone, t ) ) break;
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
if( t < tmin ) tmin = t;
else if( t > tmax ) tmax = t;
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
if( end > rangeMax || zone.Start() < rangeMin ) continue;
const auto ctx = m_worker.GetContextSwitchData( m_worker.DecompressThread( zones[i].Thread() ) );
if( !ctx ) break;
int64_t t;
if( !GetZoneRunningTime( ctx, zone, t ) ) break;
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
if( t < tmin ) tmin = t;
else if( t > tmax ) tmax = t;
}
}
else
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto ctx = m_worker.GetContextSwitchData( m_worker.DecompressThread( zones[i].Thread() ) );
if( !ctx ) break;
int64_t t;
if( !GetZoneRunningTime( ctx, zone, t ) ) break;
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
if( t < tmin ) tmin = t;
else if( t > tmax ) tmax = t;
}
}
}
else if( m_findZone.selfTime )
{
tmin = zoneData.selfMin;
tmax = zoneData.selfMax;
if( m_findZone.range.active )
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto start = zone.Start();
if( end > rangeMax || start < rangeMin ) continue;
const auto t = end - start - GetZoneChildTimeFast( zone );
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
else
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto t = end - zone.Start() - GetZoneChildTimeFast( zone );
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
}
else
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
tmin = zoneData.min;
tmax = zoneData.max;
if( m_findZone.range.active )
{
auto& zone = *zones[i].Zone();
const auto ctx = m_worker.GetContextSwitchData( m_worker.DecompressThread( zones[i].Thread() ) );
if( !ctx ) break;
int64_t t;
if( !GetZoneRunningTime( ctx, zone, t ) ) break;
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
if( t < tmin ) tmin = t;
else if( t > tmax ) tmax = t;
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto start = zone.Start();
if( end > rangeMax || start < rangeMin ) continue;
const auto t = end - start;
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
}
}
else if( m_findZone.selfTime )
{
tmin = zoneData.selfMin;
tmax = zoneData.selfMax;
if( m_findZone.range.active )
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
else
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto start = zone.Start();
if( end > rangeMax || start < rangeMin ) continue;
const auto t = end - start - GetZoneChildTimeFast( zone );
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
else
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto t = end - zone.Start() - GetZoneChildTimeFast( zone );
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto t = end - zone.Start();
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
}
}
else
{
tmin = zoneData.min;
tmax = zoneData.max;
if( m_findZone.range.active )
auto& zoneData = m_worker.GetGpuZonesForSourceLocation( matchSrcloc );
auto& zones = zoneData.zones;
if( m_findZone.selfTime )
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
tmin = zoneData.selfMin;
tmax = zoneData.selfMax;
if( m_findZone.range.active )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto start = zone.Start();
if( end > rangeMax || start < rangeMin ) continue;
const auto t = end - start;
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.GpuEnd();
const auto start = zone.GpuStart();
if( end > rangeMax || start < rangeMin ) continue;
const auto t = end - start - GetZoneChildTime( zone );
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
else
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.GpuEnd();
const auto t = end - zone.GpuStart() - GetZoneChildTime( zone );
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
}
else
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
tmin = zoneData.min;
tmax = zoneData.max;
if( m_findZone.range.active )
{
auto& zone = *zones[i].Zone();
const auto end = zone.End();
const auto t = end - zone.Start();
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.GpuEnd();
const auto start = zone.GpuStart();
if( end > rangeMax || start < rangeMin ) continue;
const auto t = end - start;
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
else
{
for( i=m_findZone.sortedNum; i<zsz; i++ )
{
auto& zone = *zones[i].Zone();
const auto end = zone.GpuEnd();
const auto t = end - zone.GpuStart();
vec.push_back_no_space_check( t );
total += t;
sumSq += double( t ) * t;
}
}
}
}
@@ -566,10 +677,12 @@ void View::DrawFindZone()
}
}
if( m_findZone.selGroup != m_findZone.Unselected )
if( !isGpu && m_findZone.selGroup != m_findZone.Unselected )
{
if( m_findZone.selSortNum != m_findZone.sortedNum )
{
auto& zoneData = m_worker.GetZonesForSourceLocation( matchSrcloc );
auto& zones = zoneData.zones;
const auto selGroup = m_findZone.selGroup;
const auto groupBy = m_findZone.groupBy;
@@ -710,7 +823,7 @@ void View::DrawFindZone()
if( ImGui::Button( "Reset" ) ) m_findZone.minBinVal = 1;
ImGui::PopStyleVar();
if( s_config.llm )
if( !isGpu && s_config.llm )
{
constexpr int LlmBins = 32;
@@ -718,7 +831,7 @@ void View::DrawFindZone()
auto& srcloc = m_worker.GetSourceLocation( m_findZone.match[m_findZone.selMatch] );
nlohmann::json json = {
{ "type", "zone_histogram" },
{ "count", zones.size() },
{ "count", zsz },
{ "source_location", {
{ "file", m_worker.GetString( srcloc.file ) },
{ "line", srcloc.line },
@@ -856,9 +969,9 @@ void View::DrawFindZone()
}
ImGui::SameLine();
char buf[64];
PrintStringPercent( buf, 100.f * zoneData.selfTotal / zoneData.total );
PrintStringPercent( buf, 100.f * selfTotal / rawTotal );
TextDisabledUnformatted( buf );
if( m_worker.HasContextSwitches() )
if( !isGpu && m_worker.HasContextSwitches() )
{
ImGui::SameLine();
if( SmallCheckbox( "Running time", &m_findZone.runningTime ) )
@@ -1550,6 +1663,15 @@ void View::DrawFindZone()
ImGui::TreePop();
}
if( isGpu )
{
ImGui::Separator();
TextDisabledUnformatted( "Zone grouping, the found-zones list, and callstack samples are not yet available for GPU zones." );
}
else
{
auto& zoneData = m_worker.GetZonesForSourceLocation( matchSrcloc );
auto& zones = zoneData.zones;
ImGui::Separator();
SmallCheckbox( "Show zone time in frames", &m_findZone.showZoneInFrames );
ImGui::Separator();
@@ -2171,6 +2293,7 @@ void View::DrawFindZone()
auto& srcloc = m_worker.GetSourceLocation( changeZone );
m_findZone.ShowZone( changeZone, m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ) );
}
}
}
ImGui::EndChild();
ImGui::End();

View File

@@ -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 ) )
{

View File

@@ -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();

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() );
@@ -3603,6 +3619,31 @@ Worker::GpuSourceLocationZones* Worker::GetGpuSourceLocationZonesReal( uint16_t
m_data.gpuZonesLast.second = &it->second;
return &it->second;
}
int64_t Worker::GetGpuChildTime( const GpuEvent& zone )
{
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;
}
#else
uint64_t* Worker::GetSourceLocationZonesCntReal( uint16_t srcloc )
{
@@ -6200,6 +6241,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 +8163,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

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
@@ -645,6 +648,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; }
@@ -933,6 +938,8 @@ private:
return GetGpuSourceLocationZonesReal( srcloc );
}
GpuSourceLocationZones* GetGpuSourceLocationZonesReal( uint16_t srcloc );
int64_t GetGpuChildTime( const GpuEvent& zone );
#else
uint64_t* GetSourceLocationZonesCnt( uint16_t srcloc )
{