Profiling data dumped to file on close
This commit is contained in:
parent
5f9ba68c99
commit
48134b2027
5 changed files with 119 additions and 67 deletions
29
Ryujinx.Profiler/DumpProfile.cs
Normal file
29
Ryujinx.Profiler/DumpProfile.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.Profiler
|
||||||
|
{
|
||||||
|
public static class DumpProfile
|
||||||
|
{
|
||||||
|
public static void ToFile(string path, InternalProfile profile)
|
||||||
|
{
|
||||||
|
String fileData = "";
|
||||||
|
foreach (var time in profile.Timers)
|
||||||
|
{
|
||||||
|
fileData += $"{time.Key} - " +
|
||||||
|
$"Total: {profile.ConvertTicksToMS(time.Value.TotalTime)}ms, " +
|
||||||
|
$"Average: {profile.ConvertTicksToMS(time.Value.AverageTime)}ms, " +
|
||||||
|
$"Count: {time.Value.Count}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure file directory exists before write
|
||||||
|
FileInfo fileInfo = new FileInfo(path);
|
||||||
|
if (fileInfo == null)
|
||||||
|
throw new Exception("Unknown logging error, probably a bad file path");
|
||||||
|
if (fileInfo.Directory != null && !fileInfo.Directory.Exists)
|
||||||
|
Directory.CreateDirectory(fileInfo.Directory.FullName);
|
||||||
|
|
||||||
|
File.WriteAllText(fileInfo.FullName, fileData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Ryujinx.Profiler/InternalProfile.cs
Normal file
65
Ryujinx.Profiler/InternalProfile.cs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ryujinx.Profiler
|
||||||
|
{
|
||||||
|
public class InternalProfile
|
||||||
|
{
|
||||||
|
private Stopwatch SW;
|
||||||
|
internal ConcurrentDictionary<string, TimingInfo> Timers;
|
||||||
|
|
||||||
|
public InternalProfile()
|
||||||
|
{
|
||||||
|
Timers = new ConcurrentDictionary<string, TimingInfo>();
|
||||||
|
SW = new Stopwatch();
|
||||||
|
SW.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginProfile(ProfileConfig config)
|
||||||
|
{
|
||||||
|
long timestamp = SW.ElapsedTicks;
|
||||||
|
|
||||||
|
Timers.AddOrUpdate(config.Name,
|
||||||
|
(string s) => CreateTimer(timestamp),
|
||||||
|
((s, info) =>
|
||||||
|
{
|
||||||
|
info.BeginTime = timestamp;
|
||||||
|
return info;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndProfile(ProfileConfig config)
|
||||||
|
{
|
||||||
|
long timestamp = SW.ElapsedTicks;
|
||||||
|
|
||||||
|
Timers.AddOrUpdate(config.Name,
|
||||||
|
(s => new TimingInfo()),
|
||||||
|
((s, time) => UpdateTimer(time, timestamp)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimingInfo CreateTimer(long timestamp)
|
||||||
|
{
|
||||||
|
return new TimingInfo()
|
||||||
|
{
|
||||||
|
BeginTime = timestamp,
|
||||||
|
LastTime = 0,
|
||||||
|
Count = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimingInfo UpdateTimer(TimingInfo time, long timestamp)
|
||||||
|
{
|
||||||
|
time.Count++;
|
||||||
|
time.LastTime = timestamp - time.BeginTime;
|
||||||
|
time.TotalTime += time.LastTime;
|
||||||
|
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double ConvertTicksToMS(long ticks)
|
||||||
|
{
|
||||||
|
return (((double)ticks) / Stopwatch.Frequency) * 1000.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Profiler
|
namespace Ryujinx.Profiler
|
||||||
{
|
{
|
||||||
public class Profile
|
public class Profile
|
||||||
{
|
{
|
||||||
private struct TimingInfo
|
|
||||||
{
|
|
||||||
public long BeginTime, LastTime, TotalTime, Count;
|
|
||||||
public long AverageTime
|
|
||||||
{
|
|
||||||
get => (Count == 0) ? -1 : TotalTime / Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
private static Profile ProfileInstance;
|
private static InternalProfile ProfileInstance;
|
||||||
private static ProfilerSettings Settings;
|
private static ProfilerSettings Settings;
|
||||||
|
|
||||||
private static bool ProfilingEnabled()
|
private static bool ProfilingEnabled()
|
||||||
|
@ -26,7 +14,7 @@ namespace Ryujinx.Profiler
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ProfileInstance == null)
|
if (ProfileInstance == null)
|
||||||
ProfileInstance = new Profile();
|
ProfileInstance = new InternalProfile();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +23,15 @@ namespace Ryujinx.Profiler
|
||||||
Settings = settings;
|
Settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void FinishProfiling()
|
||||||
|
{
|
||||||
|
if (!ProfilingEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Settings.FileDumpEnabled)
|
||||||
|
DumpProfile.ToFile(Settings.DumpLocation, ProfileInstance);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Begin(ProfileConfig config)
|
public static void Begin(ProfileConfig config)
|
||||||
{
|
{
|
||||||
if (!ProfilingEnabled())
|
if (!ProfilingEnabled())
|
||||||
|
@ -59,58 +56,5 @@ namespace Ryujinx.Profiler
|
||||||
method();
|
method();
|
||||||
End(config);
|
End(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Non-static
|
|
||||||
private Stopwatch SW;
|
|
||||||
private ConcurrentDictionary<string, TimingInfo> Timers;
|
|
||||||
|
|
||||||
public Profile()
|
|
||||||
{
|
|
||||||
Timers = new ConcurrentDictionary<string, TimingInfo>();
|
|
||||||
SW = new Stopwatch();
|
|
||||||
SW.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginProfile(ProfileConfig config)
|
|
||||||
{
|
|
||||||
long timestamp = SW.ElapsedTicks;
|
|
||||||
|
|
||||||
Timers.AddOrUpdate(config.Name,
|
|
||||||
(string s) => CreateTimer(timestamp),
|
|
||||||
((s, info) =>
|
|
||||||
{
|
|
||||||
info.BeginTime = timestamp;
|
|
||||||
return info;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndProfile(ProfileConfig config)
|
|
||||||
{
|
|
||||||
long timestamp = SW.ElapsedTicks;
|
|
||||||
|
|
||||||
Timers.AddOrUpdate(config.Name,
|
|
||||||
(s => new TimingInfo()),
|
|
||||||
((s, time) => UpdateTimer(time, timestamp)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private TimingInfo CreateTimer(long timestamp)
|
|
||||||
{
|
|
||||||
return new TimingInfo()
|
|
||||||
{
|
|
||||||
BeginTime = timestamp,
|
|
||||||
LastTime = 0,
|
|
||||||
Count = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private TimingInfo UpdateTimer(TimingInfo time, long timestamp)
|
|
||||||
{
|
|
||||||
time.Count++;
|
|
||||||
time.LastTime = timestamp - time.BeginTime;
|
|
||||||
time.TotalTime += time.LastTime;
|
|
||||||
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
Ryujinx.Profiler/TimingInfo.cs
Normal file
11
Ryujinx.Profiler/TimingInfo.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Profiler
|
||||||
|
{
|
||||||
|
public struct TimingInfo
|
||||||
|
{
|
||||||
|
public long BeginTime, LastTime, TotalTime, Count;
|
||||||
|
public long AverageTime
|
||||||
|
{
|
||||||
|
get => (Count == 0) ? -1 : TotalTime / Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -269,6 +269,9 @@ namespace Ryujinx
|
||||||
_renderThread.Join();
|
_renderThread.Join();
|
||||||
|
|
||||||
base.OnUnload(e);
|
base.OnUnload(e);
|
||||||
|
|
||||||
|
// TODO: Find a better home for this, currently only called on OGL window close
|
||||||
|
Profile.FinishProfiling();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnResize(EventArgs e)
|
protected override void OnResize(EventArgs e)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue