Memory Changes part 2

* Slightly refactors RangeLists from the last Memory Changes MR, which fixes issue 61.

* Convert as many const size array iterators to span iterators as possible. When iterating over a const size array, every iteration created a Span, now only the first iteration does in most places.

* Now using object pooling for a few object types that were rapidly deleted and recreated.

* Converted a few flag checks to binary operations to save memory allocations.
This commit is contained in:
LotP 2025-08-25 17:44:15 -05:00 committed by KeatonTheBot
parent 01cb33f658
commit 171624e7f0
89 changed files with 2121 additions and 1157 deletions

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Collections;
using Ryujinx.Common.Memory.PartialUnmaps;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
@ -8,6 +9,24 @@ using System.Threading;
namespace Ryujinx.Memory.WindowsShared
{
public class ObjectPool<T>
{
private readonly Stack<T> _objects;
private readonly Func<T> _objectGenerator;
public ObjectPool(Func<T> objectGenerator)
{
_objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator));
_objects = new Stack<T>();
}
public T Get() => _objects.Count > 0 ? _objects.Pop() : _objectGenerator();
public void Return(T item) => _objects.Push(item);
public void Clear() => _objects.Clear();
}
/// <summary>
/// Windows memory placeholder manager.
/// </summary>
@ -18,6 +37,7 @@ namespace Ryujinx.Memory.WindowsShared
private readonly MappingTree<ulong> _mappings;
private readonly MappingTree<MemoryPermission> _protections;
private readonly ObjectPool<RangeNode<MemoryPermission>> _protectionObjectPool;
private readonly IntPtr _partialUnmapStatePtr;
private readonly Thread _partialUnmapTrimThread;
@ -28,6 +48,8 @@ namespace Ryujinx.Memory.WindowsShared
{
_mappings = new MappingTree<ulong>();
_protections = new MappingTree<MemoryPermission>();
_protectionObjectPool = new ObjectPool<RangeNode<MemoryPermission>>(() => new RangeNode<MemoryPermission>());
_partialUnmapStatePtr = PartialUnmapState.GlobalState;
@ -70,12 +92,12 @@ namespace Ryujinx.Memory.WindowsShared
{
lock (_mappings)
{
_mappings.Add(new RangeNode<ulong>(address, address + size, ulong.MaxValue));
_mappings.Add(new RangeNode<ulong>().Init(address, address + size, ulong.MaxValue));
}
lock (_protections)
{
_protections.Add(new RangeNode<MemoryPermission>(address, address + size, MemoryPermission.None));
_protections.Add(_protectionObjectPool.Get().Init(address, address + size, MemoryPermission.None));
}
}
@ -214,8 +236,8 @@ namespace Ryujinx.Memory.WindowsShared
(IntPtr)size,
AllocationType.Release | AllocationType.PreservePlaceholder));
_mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
_mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart)));
_mappings.Add(new RangeNode<ulong>().Init(overlapStart, address, overlapValue));
_mappings.Add(new RangeNode<ulong>().Init(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart)));
}
else if (overlapStartsBefore)
{
@ -226,7 +248,7 @@ namespace Ryujinx.Memory.WindowsShared
(IntPtr)overlappedSize,
AllocationType.Release | AllocationType.PreservePlaceholder));
_mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
_mappings.Add(new RangeNode<ulong>().Init(overlapStart, address, overlapValue));
}
else if (overlapEndsAfter)
{
@ -237,10 +259,10 @@ namespace Ryujinx.Memory.WindowsShared
(IntPtr)overlappedSize,
AllocationType.Release | AllocationType.PreservePlaceholder));
_mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize)));
_mappings.Add(new RangeNode<ulong>().Init(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize)));
}
_mappings.Add(new RangeNode<ulong>(address, endAddress, backingOffset));
_mappings.Add(new RangeNode<ulong>().Init(address, endAddress, backingOffset));
}
}
@ -306,7 +328,7 @@ namespace Ryujinx.Memory.WindowsShared
lock (_mappings)
{
_mappings.Remove(overlap);
_mappings.Add(new RangeNode<ulong>(overlap.Start, overlap.End, ulong.MaxValue));
_mappings.Add(new RangeNode<ulong>().Init(overlap.Start, overlap.End, ulong.MaxValue));
}
bool overlapStartsBefore = overlap.Start < startAddress;
@ -433,7 +455,7 @@ namespace Ryujinx.Memory.WindowsShared
unmappedCount++;
}
_mappings.Add(new RangeNode<ulong>(address, endAddress, ulong.MaxValue));
_mappings.Add(new RangeNode<ulong>().Init(address, endAddress, ulong.MaxValue));
}
if (unmappedCount > 1)
@ -628,14 +650,16 @@ namespace Ryujinx.Memory.WindowsShared
{
if (startAddress > protAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(protAddress, startAddress, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(protAddress, startAddress, protPermission));
}
if (endAddress < protEndAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(endAddress, protEndAddress, protPermission));
}
}
_protectionObjectPool.Return(protection);
if (node.End >= endAddress)
{
@ -643,7 +667,7 @@ namespace Ryujinx.Memory.WindowsShared
}
}
_protections.Add(new RangeNode<MemoryPermission>(startAddress, endAddress, permission));
_protections.Add(_protectionObjectPool.Get().Init(startAddress, endAddress, permission));
}
}
@ -674,14 +698,16 @@ namespace Ryujinx.Memory.WindowsShared
if (address > protAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(protAddress, address, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(protAddress, address, protPermission));
}
if (endAddress < protEndAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(endAddress, protEndAddress, protPermission));
}
_protectionObjectPool.Return(protection);
if (node.End >= endAddress)
{
break;