Memory changes 3

General memory improvements to decrease GC pressure and frequency.

Pool big arrays and objects that are created and deleted often.

Skip data copies when they aren't needed.

Inline flag checks to skip unneeded allocations.

From my testing the performance is about the same, but the GC frequency is much lower and collection is faster causing less and smaller spikes.
This commit is contained in:
LotP
2025-11-23 13:43:14 -06:00
committed by KeatonTheBot
parent c1e24961f9
commit 35bced8527
42 changed files with 703 additions and 331 deletions
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@@ -38,7 +39,7 @@ namespace Ryujinx.Memory.Range
index = ~index;
}
RangeItem<T> rangeItem = new(item);
RangeItem<T> rangeItem = _rangeItemPool.Allocate().Set(item);
Insert(index, rangeItem);
}
@@ -144,6 +145,8 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveAt(int index)
{
_rangeItemPool.Release(Items[index]);
if (index < Count - 1)
{
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
@@ -433,7 +436,7 @@ namespace Ryujinx.Memory.Range
return (Items[index], Items[endIndex - 1]);
}
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size)
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size, out int length)
{
(int index, int endIndex) = BinarySearchEdges(address, address + size);
@@ -441,11 +444,13 @@ namespace Ryujinx.Memory.Range
if (index < 0)
{
result = [];
result = null;
length = 0;
}
else
{
result = new RangeItem<T>[endIndex - index];
result = ArrayPool<RangeItem<T>>.Shared.Rent(endIndex - index);
length = endIndex - index;
Array.Copy(Items, index, result, 0, endIndex - index);
}
-37
View File
@@ -36,8 +36,6 @@ namespace Ryujinx.Memory.Range
public class RangeList<T> : RangeListBase<T> where T : IRange
{
public readonly ReaderWriterLockSlim Lock = new();
private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
/// <summary>
/// Creates a new range list.
@@ -93,11 +91,6 @@ namespace Ryujinx.Memory.Range
Items[index + 1].Previous = rangeItem;
}
foreach (ulong address in Items[index].QuickAccessAddresses)
{
_quickAccess.Remove(address);
}
Items[index] = rangeItem;
return true;
@@ -142,11 +135,6 @@ namespace Ryujinx.Memory.Range
Items[index + 1].Previous = rangeItem;
}
foreach (ulong address in item.QuickAccessAddresses)
{
_quickAccess.Remove(address);
}
Items[index] = rangeItem;
return true;
@@ -210,11 +198,6 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveAt(int index)
{
foreach (ulong address in Items[index].QuickAccessAddresses)
{
_quickAccess.Remove(address);
}
if (index < Count - 1)
{
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
@@ -253,15 +236,6 @@ namespace Ryujinx.Memory.Range
int startIndex = BinarySearch(startItem.Address);
int endIndex = BinarySearch(endItem.Address);
for (int i = startIndex; i <= endIndex; i++)
{
_quickAccess.Remove(Items[i].Address);
foreach (ulong addr in Items[i].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
}
}
if (endIndex < Count - 1)
{
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
@@ -349,11 +323,6 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
{
if (_quickAccess.TryGetValue(address, out RangeItem<T> quickResult))
{
return quickResult;
}
int index = BinarySearch(address, address + size);
if (index < 0)
@@ -361,12 +330,6 @@ namespace Ryujinx.Memory.Range
return null;
}
if (Items[index].OverlapsWith(address, address + 1))
{
_quickAccess.Add(address, Items[index]);
Items[index].QuickAccessAddresses.Add(address);
}
return Items[index];
}
+29 -18
View File
@@ -1,20 +1,42 @@
using System.Collections;
using Ryujinx.Common;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Ryujinx.Memory.Range
{
public class RangeItem<TValue>(TValue value) where TValue : IRange
public class RangeItem<TValue> where TValue : IRange
{
public RangeItem<TValue> Next;
public RangeItem<TValue> Previous;
public readonly ulong Address = value.Address;
public readonly ulong EndAddress = value.Address + value.Size;
public ulong Address;
public ulong EndAddress;
public readonly TValue Value = value;
public TValue Value;
public RangeItem()
{
}
public RangeItem(TValue value)
{
Address = value.Address;
EndAddress = value.Address + value.Size;
Value = value;
}
public readonly List<ulong> QuickAccessAddresses = [];
public RangeItem<TValue> Set(TValue value)
{
Next = null;
Previous = null;
Address = value.Address;
EndAddress = value.Address + value.Size;
Value = value;
return this;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool OverlapsWith(ulong address, ulong endAddress)
@@ -23,20 +45,9 @@ namespace Ryujinx.Memory.Range
}
}
class AddressEqualityComparer : IEqualityComparer<ulong>
{
public bool Equals(ulong u1, ulong u2)
{
return u1 == u2;
}
public int GetHashCode(ulong value) => (int)(value << 5);
public static readonly AddressEqualityComparer Comparer = new();
}
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
{
protected static readonly ObjectPool<RangeItem<T>> _rangeItemPool = new(() => new RangeItem<T>());
private const int BackingInitialSize = 1024;
protected RangeItem<T>[] Items;
+10 -4
View File
@@ -1,5 +1,6 @@
using Ryujinx.Memory.Range;
using System;
using System.Buffers;
using System.Collections.Generic;
namespace Ryujinx.Memory.Tracking
@@ -300,10 +301,10 @@ namespace Ryujinx.Memory.Tracking
// We use the non-span method here because keeping the lock will cause a deadlock.
regions.Lock.EnterReadLock();
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size);
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size, out int length);
regions.Lock.ExitReadLock();
if (overlaps.Length == 0 && !precise)
if (length == 0 && !precise)
{
if (_memoryManager.IsRangeMapped(address, size))
{
@@ -323,8 +324,8 @@ namespace Ryujinx.Memory.Tracking
// Increase the access size to trigger handles with misaligned accesses.
size += (ulong)_pageSize;
}
for (int i = 0; i < overlaps.Length; i++)
for (int i = 0; i < length; i++)
{
VirtualRegion region = overlaps[i].Value;
@@ -337,6 +338,11 @@ namespace Ryujinx.Memory.Tracking
region.Signal(address, size, write, exemptId);
}
}
if (length != 0)
{
ArrayPool<RangeItem<VirtualRegion>>.Shared.Return(overlaps);
}
}
}