| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Ryujinx.Common;
- namespace Ryujinx.Profiler
- {
- public class InternalProfile
- {
- private struct TimerQueueValue
- {
- public ProfileConfig Config;
- public long Time;
- public bool IsBegin;
- }
- internal Dictionary<ProfileConfig, TimingInfo> Timers { get; set; }
- private readonly object _timerQueueClearLock = new object();
- private ConcurrentQueue<TimerQueueValue> _timerQueue;
- private int _sessionCounter = 0;
- // Cleanup thread
- private readonly Thread _cleanupThread;
- private bool _cleanupRunning;
- private readonly long _history;
- private long _preserve;
- // Timing flags
- private TimingFlag[] _timingFlags;
- private long[] _timingFlagAverages;
- private long[] _timingFlagLast;
- private long[] _timingFlagLastDelta;
- private int _timingFlagCount;
- private int _timingFlagIndex;
- private int _maxFlags;
- private Action<TimingFlag> _timingFlagCallback;
- public InternalProfile(long history, int maxFlags)
- {
- _maxFlags = maxFlags;
- Timers = new Dictionary<ProfileConfig, TimingInfo>();
- _timingFlags = new TimingFlag[_maxFlags];
- _timingFlagAverages = new long[(int)TimingFlagType.Count];
- _timingFlagLast = new long[(int)TimingFlagType.Count];
- _timingFlagLastDelta = new long[(int)TimingFlagType.Count];
- _timerQueue = new ConcurrentQueue<TimerQueueValue>();
- _history = history;
- _cleanupRunning = true;
- // Create cleanup thread.
- _cleanupThread = new Thread(CleanupLoop);
- _cleanupThread.Start();
- }
- private void CleanupLoop()
- {
- bool queueCleared = false;
- while (_cleanupRunning)
- {
- // Ensure we only ever have 1 instance modifying timers or timerQueue
- if (Monitor.TryEnter(_timerQueueClearLock))
- {
- queueCleared = ClearTimerQueue();
- // Calculate before foreach to mitigate redundant calculations
- long cleanupBefore = PerformanceCounter.ElapsedTicks - _history;
- long preserveStart = _preserve - _history;
- // Each cleanup is self contained so run in parallel for maximum efficiency
- Parallel.ForEach(Timers, (t) => t.Value.Cleanup(cleanupBefore, preserveStart, _preserve));
- Monitor.Exit(_timerQueueClearLock);
- }
- // Only sleep if queue was successfully cleared
- if (queueCleared)
- {
- Thread.Sleep(5);
- }
- }
- }
- private bool ClearTimerQueue()
- {
- int count = 0;
- while (_timerQueue.TryDequeue(out TimerQueueValue item))
- {
- if (!Timers.TryGetValue(item.Config, out TimingInfo value))
- {
- value = new TimingInfo();
- Timers.Add(item.Config, value);
- }
- if (item.IsBegin)
- {
- value.Begin(item.Time);
- }
- else
- {
- value.End(item.Time);
- }
- // Don't block for too long as memory disposal is blocked while this function runs
- if (count++ > 10000)
- {
- return false;
- }
- }
- return true;
- }
- public void FlagTime(TimingFlagType flagType)
- {
- int flagId = (int)flagType;
- _timingFlags[_timingFlagIndex] = new TimingFlag()
- {
- FlagType = flagType,
- Timestamp = PerformanceCounter.ElapsedTicks
- };
- _timingFlagCount = Math.Max(_timingFlagCount + 1, _maxFlags);
- // Work out average
- if (_timingFlagLast[flagId] != 0)
- {
- _timingFlagLastDelta[flagId] = _timingFlags[_timingFlagIndex].Timestamp - _timingFlagLast[flagId];
- _timingFlagAverages[flagId] = (_timingFlagAverages[flagId] == 0) ? _timingFlagLastDelta[flagId] :
- (_timingFlagLastDelta[flagId] + _timingFlagAverages[flagId]) >> 1;
- }
- _timingFlagLast[flagId] = _timingFlags[_timingFlagIndex].Timestamp;
- // Notify subscribers
- _timingFlagCallback?.Invoke(_timingFlags[_timingFlagIndex]);
- if (++_timingFlagIndex >= _maxFlags)
- {
- _timingFlagIndex = 0;
- }
- }
- public void BeginProfile(ProfileConfig config)
- {
- _timerQueue.Enqueue(new TimerQueueValue()
- {
- Config = config,
- IsBegin = true,
- Time = PerformanceCounter.ElapsedTicks,
- });
- }
- public void EndProfile(ProfileConfig config)
- {
- _timerQueue.Enqueue(new TimerQueueValue()
- {
- Config = config,
- IsBegin = false,
- Time = PerformanceCounter.ElapsedTicks,
- });
- }
- public string GetSession()
- {
- // Can be called from multiple threads so we need to ensure no duplicate sessions are generated
- return Interlocked.Increment(ref _sessionCounter).ToString();
- }
- public List<KeyValuePair<ProfileConfig, TimingInfo>> GetProfilingData()
- {
- _preserve = PerformanceCounter.ElapsedTicks;
- lock (_timerQueueClearLock)
- {
- ClearTimerQueue();
- return Timers.ToList();
- }
- }
- public TimingFlag[] GetTimingFlags()
- {
- int count = Math.Max(_timingFlagCount, _maxFlags);
- TimingFlag[] outFlags = new TimingFlag[count];
-
- for (int i = 0, sourceIndex = _timingFlagIndex; i < count; i++, sourceIndex++)
- {
- if (sourceIndex >= _maxFlags)
- sourceIndex = 0;
- outFlags[i] = _timingFlags[sourceIndex];
- }
- return outFlags;
- }
- public (long[], long[]) GetTimingAveragesAndLast()
- {
- return (_timingFlagAverages, _timingFlagLastDelta);
- }
- public void RegisterFlagReceiver(Action<TimingFlag> receiver)
- {
- _timingFlagCallback = receiver;
- }
- public void Dispose()
- {
- _cleanupRunning = false;
- _cleanupThread.Join();
- }
- }
- }
|