diff options
| author | Konstantin <const@const.me> | 2023-01-20 15:25:02 +0100 |
|---|---|---|
| committer | Konstantin <const@const.me> | 2023-01-20 15:25:02 +0100 |
| commit | 5483755f6332dc0f00174a9f7869f44073f4359d (patch) | |
| tree | 7caafd1d3185ec0ced82afa3996ae537a5759bd9 /Tools | |
| parent | a052894a31128ef62e53ae34efd3485c2bbd8569 (diff) | |
A tool to summarize performance data into one table
Diffstat (limited to 'Tools')
| -rw-r--r-- | Tools/PerfSummary/LogParser.cs | 207 | ||||
| -rw-r--r-- | Tools/PerfSummary/PerfSummary.cs | 29 | ||||
| -rw-r--r-- | Tools/PerfSummary/PerfSummary.csproj | 10 | ||||
| -rw-r--r-- | Tools/PerfSummary/Summary.cs | 88 |
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 |
