From f48247126e691d5d252c01206ddd3c3a90c5db1e Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Thu, 5 Nov 2026 15:49:57 +0100 Subject: [PATCH] Add frame statistics export to csvexport. --- csvexport/src/csvexport.cpp | 97 ++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/csvexport/src/csvexport.cpp b/csvexport/src/csvexport.cpp index 0a0ad308..88ae4e78 100644 --- a/csvexport/src/csvexport.cpp +++ b/csvexport/src/csvexport.cpp @@ -30,6 +30,7 @@ static void print_usage_exit( int e ) fprintf( stderr, " -V, --version Show version information\n" ); fprintf( stderr, " -f, --filter arg Filter zone names (default: " ")\n" ); + fprintf( stderr, " -F, --frames Report frame statistics, one row per frame set\n" ); fprintf( stderr, " -s, --sep arg CSV separator (default: ,)\n" ); fprintf( stderr, " -c, --case Case sensitive filtering\n" ); fprintf( stderr, " -e, --self Get self times\n" ); @@ -53,6 +54,7 @@ struct Args bool show_gpu; bool unwrapMessages; bool plot; + bool frames; int truncated_mean_percentile; }; @@ -60,12 +62,13 @@ static Args parse_args( int argc, char** argv ) { if( argc == 1 ) print_usage_exit( 1 ); - Args args = { "", ",", "", false, false, false, false, false, false, 0 }; + Args args = { "", ",", "", false, false, false, false, false, false, false, 0 }; struct option long_opts[] = { { "help", no_argument, nullptr, 'h' }, { "version", no_argument, nullptr, 'V' }, { "filter", required_argument, nullptr, 'f' }, + { "frames", no_argument, nullptr, 'F' }, { "sep", required_argument, nullptr, 's' }, { "case", no_argument, nullptr, 'c' }, { "self", no_argument, nullptr, 'e' }, @@ -78,7 +81,7 @@ static Args parse_args( int argc, char** argv ) }; int c; - while( ( c = getopt_long( argc, argv, "hf:s:t:ceugmpV", long_opts, nullptr ) ) != -1 ) + while( ( c = getopt_long( argc, argv, "hf:Fs:t:ceugmpV", long_opts, nullptr ) ) != -1 ) { switch( c ) { @@ -91,6 +94,9 @@ static Args parse_args( int argc, char** argv ) case 'f': args.filter = optarg; break; + case 'F': + args.frames = true; + break; case 's': args.separator = optarg; break; @@ -299,6 +305,93 @@ int main( int argc, char** argv ) std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) ); } + if( args.frames ) + { + // The base frame set always holds two bootstrap events; the profiler + // only considers frames present past that point. + if( !worker.AreFramesUsed() ) + { + printf( "There are currently no frames!\n" ); + return 0; + } + + const auto& frames = worker.GetFrames(); + + std::vector columns { + "frame_set", + "continuous", + "frames", + "counts", + "total_ns", + "span_perc", + "min_ns", + "max_ns", + "mean_ns", + "median_ns", + "std_ns", + "cv_perc", + "p75_ns", + "p90_ns", + "p99_ns", + "p99_9_ns" + }; + + std::string header = join( columns, args.separator ); + printf( "%s\n", header.data() ); + + const auto span = worker.GetLastTime(); + for( const auto& fd : frames ) + { + std::vector times; + const auto fsz = worker.GetFullFrameCount( *fd ); + times.reserve( fsz ); + int64_t total = 0; + double sumSq = 0; + for( size_t i=0; i 0 ) + { + times.push_back( t ); + total += t; + sumSq += double( t ) * t; + } + } + const auto vsz = times.size(); + if( vsz == 0 ) continue; + std::ranges::sort( times ); + + // Same definitions as the profiler's Frame statistics window: nearest-rank + // percentiles and sample (n-1) standard deviation. + const auto Percentile = [×, vsz]( double p ) { return times[std::min( vsz - 1, size_t( p * vsz ) )]; }; + const double avg = double( total ) / vsz; + double sd = 0; + if( vsz > 1 ) sd = sqrt( ( sumSq - 2. * double( total ) * avg + avg * avg * vsz ) / ( vsz - 1 ) ); + + std::vector values( columns.size() ); + values[0] = worker.GetString( fd->name ); + values[1] = fd->continuous ? "1" : "0"; + values[2] = std::to_string( fsz ); + values[3] = std::to_string( vsz ); + values[4] = std::to_string( total ); + values[5] = span > 0 ? std::to_string( 100. * total / span ) : "0"; + values[6] = std::to_string( times.front() ); + values[7] = std::to_string( times.back() ); + values[8] = std::to_string( avg ); + values[9] = std::to_string( Percentile( 0.5 ) ); + values[10] = std::to_string( sd ); + values[11] = std::to_string( 100. * sd / avg ); + values[12] = std::to_string( Percentile( 0.75 ) ); + values[13] = std::to_string( Percentile( 0.9 ) ); + values[14] = std::to_string( Percentile( 0.99 ) ); + values[15] = std::to_string( Percentile( 0.999 ) ); + + std::string row = join( values, args.separator ); + printf( "%s\n", row.data() ); + } + return 0; + } + if( args.show_gpu ) { auto& gpu_slz = worker.GetGpuSourceLocationZones();