summaryrefslogtreecommitdiffstats
path: root/Tools/PerfSummary
diff options
context:
space:
mode:
authorKonstantin <const@const.me>2023-01-20 15:25:02 +0100
committerKonstantin <const@const.me>2023-01-20 15:25:02 +0100
commit5483755f6332dc0f00174a9f7869f44073f4359d (patch)
tree7caafd1d3185ec0ced82afa3996ae537a5759bd9 /Tools/PerfSummary
parenta052894a31128ef62e53ae34efd3485c2bbd8569 (diff)
A tool to summarize performance data into one table
Diffstat (limited to 'Tools/PerfSummary')
-rw-r--r--Tools/PerfSummary/LogParser.cs207
-rw-r--r--Tools/PerfSummary/PerfSummary.cs29
-rw-r--r--Tools/PerfSummary/PerfSummary.csproj10
-rw-r--r--Tools/PerfSummary/Summary.cs88
4 files changed, 334 insertions, 0 deletions
diff --git a/Tools/PerfSummary/LogParser.cs b/Tools/PerfSummary/LogParser.cs
new file mode 100644
index 0000000..c89d695
--- /dev/null
+++ b/Tools/PerfSummary/LogParser.cs
@@ -0,0 +1,207 @@
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+namespace PerfSummary
+{
+ enum eInputClip: byte
+ {
+ jfk,
+ columbia,
+ }
+ enum eWhisperModel: byte
+ {
+ medium,
+ large
+ }
+
+ struct LogName
+ {
+ public readonly eInputClip clip;
+ public readonly eWhisperModel model;
+ public readonly string gpu;
+
+ public override string ToString() => $"{clip}-{model}-{gpu}";
+
+ public static LogName? tryParse( string path )
+ {
+ string? ext = Path.GetExtension( path );
+ if( ext == null || !ext.Equals( ".txt", StringComparison.InvariantCultureIgnoreCase ) )
+ return null;
+
+ string name = Path.GetFileNameWithoutExtension( path );
+ string[] fields = name.Split( '-' );
+ if( fields.Length != 3 )
+ return null;
+
+ return new LogName( fields );
+ }
+
+ LogName( string[] fields )
+ {
+ clip = Enum.Parse<eInputClip>( fields[ 0 ] );
+ model = Enum.Parse<eWhisperModel>( fields[ 1 ] );
+ gpu = fields[ 2 ];
+ }
+ }
+
+ record class LogData
+ {
+ public LogName name { get; init; }
+ // The numbers are seconds
+ public double runComplete { get; init; }
+ public double encode { get; init; }
+ public double decode { get; init; }
+ // The numbers are megabytes
+ public double ram { get; init; }
+ public double vram { get; init; }
+ }
+
+ static class LogParser
+ {
+ public static IEnumerable<LogData> parse( string folder )
+ {
+ foreach( string path in Directory.EnumerateFiles( folder, "*.txt" ) )
+ {
+ LogName? name = LogName.tryParse( path );
+ if( name == null )
+ continue;
+ yield return parseFile( name.Value, path );
+ }
+ }
+
+ enum eSection: byte
+ {
+ CPU, GPU, Shaders, Memory
+ }
+ static readonly (string, eSection)[] sectionMarkers = new (string, eSection)[]
+ {
+ ("CPU Tasks", eSection.CPU),
+ ("GPU Tasks", eSection.GPU),
+ ("Compute Shaders", eSection.Shaders),
+ ("Memory Usage", eSection.Memory),
+ };
+
+ static bool tryParseSection( ref eSection? section, string line )
+ {
+ foreach( (string marker, eSection s) in sectionMarkers )
+ {
+ if( line.Contains( marker ) )
+ {
+ section = s;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool tryParseTime( ref double? val, string key, string line )
+ {
+ if( !line.StartsWith( key ) )
+ return false;
+ if( !char.IsWhiteSpace( line[ key.Length ] ) )
+ return false;
+
+ line = line.Substring( key.Length ).TrimStart();
+ int comma = line.IndexOf( ',' );
+ if( comma > 0 )
+ line = line.Substring( 0, comma );
+ string[] fields = line.Split( ' ', StringSplitOptions.RemoveEmptyEntries );
+ if( fields.Length != 2 )
+ throw new ArgumentException();
+
+ double v = double.Parse( fields[ 0 ], CultureInfo.InvariantCulture );
+ switch( fields[ 1 ] )
+ {
+ case "seconds":
+ val = v;
+ return true;
+ case "milliseconds":
+ val = v / 1E3;
+ return true;
+ case "microseconds":
+ val = v / 1E6;
+ return true;
+ }
+ throw new ArgumentException();
+ }
+
+ static readonly Regex reMemory = new Regex( @"^Total\s+([0-9\.]+)\s+(\S+)\s+RAM, ([0-9\.]+)\s+(\S+)\s+VRAM$" );
+
+ static double parseMemory( Match m, int iv )
+ {
+ double v = double.Parse( m.Groups[ iv ].Value, CultureInfo.InvariantCulture );
+ string u = m.Groups[ iv + 1 ].Value;
+ switch( u )
+ {
+ case "bytes":
+ return v / ( 1 << 20 );
+ case "KB":
+ return v / ( 1 << 10 );
+ case "MB":
+ return v;
+ case "GB":
+ return v * ( 1 << 10 );
+ }
+ throw new ArgumentException();
+ }
+
+ static bool tryParseMemory( ref double? ram, ref double? vram, string line )
+ {
+ Match m = reMemory.Match( line );
+ if( !m.Success )
+ return false;
+ ram = parseMemory( m, 1 );
+ vram = parseMemory( m, 3 );
+ return true;
+ }
+
+ static LogData parseFile( in LogName name, string path )
+ {
+ using var reader = File.OpenText( path );
+ double? runComplete = null;
+ double? encode = null;
+ double? decode = null;
+ double? ram = null;
+ double? vram = null;
+ eSection? section = null;
+
+ while( true )
+ {
+ string? line = reader.ReadLine();
+ if( line == null )
+ break;
+ if( string.IsNullOrEmpty( line ) )
+ continue;
+ if( tryParseSection( ref section, line ) )
+ continue;
+
+ switch( section )
+ {
+ case eSection.CPU:
+ tryParseTime( ref runComplete, "RunComplete", line );
+ break;
+ case eSection.GPU:
+ tryParseTime( ref encode, "Encode", line );
+ tryParseTime( ref decode, "Decode", line );
+ break;
+ case eSection.Memory:
+ tryParseMemory( ref ram, ref vram, line );
+ break;
+ }
+ }
+
+ if( null == runComplete || null == encode || null == decode || null == ram || null == vram )
+ throw new ArgumentException();
+
+ return new LogData()
+ {
+ name = name,
+ runComplete = runComplete.Value,
+ encode = encode.Value,
+ decode = decode.Value,
+ ram = ram.Value,
+ vram = vram.Value,
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Tools/PerfSummary/PerfSummary.cs b/Tools/PerfSummary/PerfSummary.cs
new file mode 100644
index 0000000..d957a96
--- /dev/null
+++ b/Tools/PerfSummary/PerfSummary.cs
@@ -0,0 +1,29 @@
+using System.Runtime.CompilerServices;
+
+namespace PerfSummary
+{
+ internal class Program
+ {
+ static string getSolutionRoot( [CallerFilePath] string? path = null )
+ {
+ string? dir = Path.GetDirectoryName( path );
+ dir = Path.GetDirectoryName( dir );
+ dir = Path.GetDirectoryName( dir );
+ return dir ?? throw new ApplicationException();
+ }
+
+ static void Main( string[] args )
+ {
+ string root = getSolutionRoot();
+ root = Path.Combine( root, "SampleClips" );
+
+ LogData[] logs = LogParser.parse( root )
+ .OrderBy( x => x.name.clip )
+ .ThenBy( x => x.name.model )
+ .ThenBy( x => x.name.gpu )
+ .ToArray();
+
+ Summary.print( logs, root );
+ }
+ }
+} \ No newline at end of file
diff --git a/Tools/PerfSummary/PerfSummary.csproj b/Tools/PerfSummary/PerfSummary.csproj
new file mode 100644
index 0000000..dee1710
--- /dev/null
+++ b/Tools/PerfSummary/PerfSummary.csproj
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net6.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/Tools/PerfSummary/Summary.cs b/Tools/PerfSummary/Summary.cs
new file mode 100644
index 0000000..8cea958
--- /dev/null
+++ b/Tools/PerfSummary/Summary.cs
@@ -0,0 +1,88 @@
+using System.Globalization;
+
+namespace PerfSummary
+{
+ static class Summary
+ {
+ public static void print( LogData[] logs, string folder )
+ {
+ string path = Path.Combine( folder, "summary.tsv" );
+ using var writer = File.CreateText( path );
+
+ string[] header = new string[]
+ {
+ "Audio Clip", "Model", "GPU",
+ "Total, sec", "Relative speed",
+ "Encode, sec", "Decode, sec",
+ "RAM, MB", "VRAM, MB"
+ };
+ writer.fields( header );
+
+ foreach( var item in logs )
+ writer.fields( item.makeFields() );
+ }
+
+ static IEnumerable<string> makeFields( this LogData log )
+ {
+ yield return log.clip();
+ yield return log.name.model.ToString();
+ yield return log.gpu();
+ yield return log.runComplete.print();
+ yield return log.relative();
+ yield return log.encode.print();
+ yield return log.decode.print();
+ yield return log.ram.print();
+ yield return log.vram.print();
+ }
+
+ static string print( this double v ) =>
+ v.ToString( CultureInfo.InvariantCulture );
+
+ static string relative( this LogData log )
+ {
+ double duration;
+ switch( log.name.clip )
+ {
+ case eInputClip.jfk:
+ duration = 11;
+ break;
+ case eInputClip.columbia:
+ duration = TimeSpan.FromTicks( 1987620000 ).TotalSeconds;
+ break;
+ default:
+ throw new NotImplementedException();
+ }
+
+ double rel = duration / log.runComplete;
+ return rel.print();
+ }
+
+ static string clip( this LogData log )
+ {
+ return log.name.clip switch
+ {
+ eInputClip.jfk => "jfk.wav",
+ eInputClip.columbia => "columbia.wma",
+ _ => throw new ArgumentException()
+ };
+ }
+
+ static string gpu( this LogData log )
+ {
+ return log.name.gpu switch
+ {
+ "1080ti" => "GeForce 1080Ti",
+ "1650" => "GeForce 1650",
+ "vega7" => "Ryzen 5 5600U",
+ "vega8" => "Ryzen 7 5700G",
+ _ => log.name.gpu
+ };
+ }
+
+ static void fields( this StreamWriter writer, IEnumerable<string> fields )
+ {
+ string line = string.Join( "\t", fields );
+ writer.WriteLine( line );
+ }
+ }
+} \ No newline at end of file