From 68f6b79fd3f06ea572d5f0edd5fc8cbaee1ae449 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Tue, 12 Jan 2021 19:04:02 +0100 Subject: [PATCH] Add a simple Pools Limiter. (#1830) * Added support for offline invalidation, via PPTC, of low cq translations replaced by high cq translations; both on a single run and between runs. Added invalidation of .cache files in the event of reuse on a different user operating system. Added .info and .cache files invalidation in case of a failed stream decompression. Nits. * InternalVersion = 1712; * Nits. * Address comment. * Get rid of BinaryFormatter. Nits. * Move Ptc.LoadTranslations(). Nits. * Nits. * Fixed corner cases (in case backup copies have to be used). Added save logs. * Not core fixes. * Complement to the previous commit. Added load logs. Removed BinaryFormatter leftovers. * Add LoadTranslations log. * Nits. * Removed the search and management of LowCq overlapping functions. * Final increment of .info and .cache flags. * Nit. * Free up memory allocated by Pools during any PPTC translations at boot time. * Nit due to rebase. * Add a simple Pools Limiter. * Nits. * Fix missing JumpTable.RegisterFunction() due to rebase. Clear MemoryStreams as soon as possible, when they are no longer needed. * Code cleaning. * Nit for retrigger Checks. * Update Ptc.cs * Contextual refactoring of Translator. Ignore resetting of pools for DirectCallStubs. * Nit for retrigger Checks. --- ARMeilleure/Common/ThreadStaticPool.cs | 84 +++++++++++++--------- ARMeilleure/Translation/DirectCallStubs.cs | 2 - ARMeilleure/Translation/PTC/Ptc.cs | 16 +++-- ARMeilleure/Translation/Translator.cs | 33 +++++---- Ryujinx.Memory/MemoryBlock.cs | 4 ++ 5 files changed, 87 insertions(+), 52 deletions(-) diff --git a/ARMeilleure/Common/ThreadStaticPool.cs b/ARMeilleure/Common/ThreadStaticPool.cs index 3fce28ec0..e23bf1dce 100644 --- a/ARMeilleure/Common/ThreadStaticPool.cs +++ b/ARMeilleure/Common/ThreadStaticPool.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Threading; namespace ARMeilleure.Common { class ThreadStaticPool where T : class, new() { - private const int PoolSizeIncrement = 200; + private const int ChunkSizeLimit = 1000; // even + private const int PoolSizeIncrement = 200; // > 0 [ThreadStatic] private static ThreadStaticPool _instance; @@ -25,11 +25,11 @@ namespace ARMeilleure.Common } } - private static ConcurrentDictionary>> _pools = new ConcurrentDictionary>>(); + private static ConcurrentDictionary>> _pools = new(); private static Stack> GetPools(int groupId) { - return _pools.GetOrAdd(groupId, x => new Stack>()); + return _pools.GetOrAdd(groupId, (groupId) => new()); } public static void PreparePool(int groupId) @@ -41,19 +41,20 @@ namespace ARMeilleure.Common var pools = GetPools(groupId); lock (pools) { - _instance = (pools.Count != 0) ? pools.Pop() : new ThreadStaticPool(PoolSizeIncrement * 2); + _instance = (pools.Count != 0) ? pools.Pop() : new(); } } } public static void ReturnPool(int groupId) { - // Reset and return the pool for this thread to the specified group. + // Reset, limit if necessary, and return the pool for this thread to the specified group. var pools = GetPools(groupId); lock (pools) { _instance.Clear(); + _instance.ChunkSizeLimiter(); pools.Push(_instance); _instance = null; @@ -66,58 +67,75 @@ namespace ARMeilleure.Common foreach (var pools in _pools.Values) { + foreach (var instance in pools) + { + instance.Dispose(); + } + pools.Clear(); } _pools.Clear(); } - private T[] _pool; - private int _poolUsed = -1; - private int _poolSize; + private List _pool; + private int _chunkIndex = -1; + private int _poolIndex = -1; - public ThreadStaticPool(int initialSize) + private ThreadStaticPool() { - _pool = new T[initialSize]; + _pool = new(ChunkSizeLimit * 2); - for (int i = 0; i < initialSize; i++) - { - _pool[i] = new T(); - } - - _poolSize = initialSize; + AddChunkIfNeeded(); } public T Allocate() { - int index = Interlocked.Increment(ref _poolUsed); - - if (index >= _poolSize) + if (++_poolIndex >= PoolSizeIncrement) { - IncreaseSize(); + AddChunkIfNeeded(); + + _poolIndex = 0; } - return _pool[index]; + return _pool[_chunkIndex][_poolIndex]; } - private void IncreaseSize() + private void AddChunkIfNeeded() { - _poolSize += PoolSizeIncrement; - - T[] newArray = new T[_poolSize]; - Array.Copy(_pool, 0, newArray, 0, _pool.Length); - - for (int i = _pool.Length; i < _poolSize; i++) + if (++_chunkIndex >= _pool.Count) { - newArray[i] = new T(); - } + T[] pool = new T[PoolSizeIncrement]; - Interlocked.Exchange(ref _pool, newArray); + for (int i = 0; i < PoolSizeIncrement; i++) + { + pool[i] = new T(); + } + + _pool.Add(pool); + } } public void Clear() { - _poolUsed = -1; + _chunkIndex = 0; + _poolIndex = -1; + } + + private void ChunkSizeLimiter() + { + if (_pool.Count >= ChunkSizeLimit) + { + int newChunkSize = ChunkSizeLimit / 2; + + _pool.RemoveRange(newChunkSize, _pool.Count - newChunkSize); + _pool.Capacity = ChunkSizeLimit * 2; + } + } + + private void Dispose() + { + _pool.Clear(); } } } diff --git a/ARMeilleure/Translation/DirectCallStubs.cs b/ARMeilleure/Translation/DirectCallStubs.cs index 57397d14b..df7ca16e7 100644 --- a/ARMeilleure/Translation/DirectCallStubs.cs +++ b/ARMeilleure/Translation/DirectCallStubs.cs @@ -34,8 +34,6 @@ namespace ARMeilleure.Translation _indirectCallStubPtr = Marshal.GetFunctionPointerForDelegate(GenerateIndirectCallStub(false)); _indirectTailCallStubPtr = Marshal.GetFunctionPointerForDelegate(GenerateIndirectCallStub(true)); - Translator.ResetPools(); - _initialized = true; } } diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index f382cc637..8f250a552 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -98,7 +98,6 @@ namespace ARMeilleure.Translation.PTC ClearMemoryStreams(); PtcJumpTable.Clear(); - PtcProfiler.Stop(); PtcProfiler.Wait(); PtcProfiler.ClearEntries(); @@ -345,6 +344,8 @@ namespace ARMeilleure.Translation.PTC private static void Save(string fileName) { + int translatedFuncsCount; + using (MemoryStream stream = new MemoryStream()) using (MD5 md5 = MD5.Create()) { @@ -361,6 +362,11 @@ namespace ARMeilleure.Translation.PTC PtcJumpTable.Serialize(stream, PtcJumpTable); + translatedFuncsCount = GetInfosEntriesCount(); + + ClearMemoryStreams(); + PtcJumpTable.Clear(); + stream.Seek((long)hashSize, SeekOrigin.Begin); byte[] hash = md5.ComputeHash(stream); @@ -388,7 +394,7 @@ namespace ARMeilleure.Translation.PTC long fileSize = new FileInfo(fileName).Length; - Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()})."); + Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {translatedFuncsCount})."); } private static void WriteHeader(MemoryStream stream) @@ -709,10 +715,10 @@ namespace ARMeilleure.Translation.PTC threads.Clear(); - _loggerEvent.Set(); - Translator.ResetPools(); + _loggerEvent.Set(); + PtcJumpTable.Initialize(jumpTable); PtcJumpTable.ReadJumpTable(jumpTable); @@ -736,7 +742,7 @@ namespace ARMeilleure.Translation.PTC Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated"); } - internal static void WriteInfoCodeReloc(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo) + internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo) { lock (_lock) { diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 612f66479..2c32e9f00 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -201,15 +201,14 @@ namespace ARMeilleure.Translation { ArmEmitterContext context = new ArmEmitterContext(memory, jumpTable, address, highCq, Aarch32Mode.User); - PrepareOperandPool(highCq); - PrepareOperationPool(highCq); - Logger.StartPass(PassName.Decoding); Block[] blocks = Decoder.Decode(memory, address, mode, highCq, singleBlock: false); Logger.EndPass(PassName.Decoding); + PreparePool(highCq); + Logger.StartPass(PassName.Translation); EmitSynchronization(context); @@ -240,23 +239,33 @@ namespace ARMeilleure.Translation if (Ptc.State == PtcState.Disabled) { func = Compiler.Compile(cfg, argTypes, OperandType.I64, options); + + ReturnPool(highCq); } - else + else using (PtcInfo ptcInfo = new PtcInfo()) { - using (PtcInfo ptcInfo = new PtcInfo()) - { - func = Compiler.Compile(cfg, argTypes, OperandType.I64, options, ptcInfo); + func = Compiler.Compile(cfg, argTypes, OperandType.I64, options, ptcInfo); - Ptc.WriteInfoCodeReloc(address, funcSize, highCq, ptcInfo); - } + ReturnPool(highCq); + + Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, highCq, ptcInfo); } - ReturnOperandPool(highCq); - ReturnOperationPool(highCq); - return new TranslatedFunction(func, funcSize, highCq); } + internal static void PreparePool(bool highCq) + { + PrepareOperandPool(highCq); + PrepareOperationPool(highCq); + } + + internal static void ReturnPool(bool highCq) + { + ReturnOperandPool(highCq); + ReturnOperationPool(highCq); + } + internal static void ResetPools() { ResetOperandPools(); diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 198e275b4..fadd50d44 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -40,6 +40,8 @@ namespace Ryujinx.Memory } Size = size; + + GC.AddMemoryPressure((long)Size); } /// @@ -281,6 +283,8 @@ namespace Ryujinx.Memory if (ptr != IntPtr.Zero) { MemoryManagement.Free(ptr); + + GC.RemoveMemoryPressure((long)Size); } }