Memory changes 2.2

A few more internal changes to the RangeList systems.

* No longer using a QuickAccess dictionary.

  * The performance of the dictionary wasn't much faster than just doing binary searches.

  * Using just binary searches allows us to take advantage of span and array returns as they're are faster than linked lists when iterating or copying the overlaps.

Small code optimizations.

Fixes a few leftover crashes.
This commit is contained in:
LotP
2025-09-12 21:27:50 -05:00
committed by KeatonTheBot
parent e3ef1e1fde
commit 65caa1e3f2
14 changed files with 240 additions and 237 deletions
@@ -12,8 +12,8 @@ namespace Ryujinx.Memory.Range
/// <typeparam name="T">Type of the range.</typeparam>
public unsafe class NonOverlappingRangeList<T> : RangeListBase<T> where T : class, INonOverlappingRange
{
private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
private readonly Dictionary<ulong, RangeItem<T>> _fastQuickAccess = new(AddressEqualityComparer.Comparer);
// private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
// private readonly Dictionary<ulong, RangeItem<T>> _fastQuickAccess = new(AddressEqualityComparer.Comparer);
public readonly ReaderWriterLockSlim Lock = new();
@@ -45,7 +45,7 @@ namespace Ryujinx.Memory.Range
Insert(index, rangeItem);
_quickAccess.Add(item.Address, rangeItem);
// _quickAccess.Add(item.Address, rangeItem);
}
/// <summary>
@@ -71,15 +71,15 @@ namespace Ryujinx.Memory.Range
Items[index + 1].Previous = rangeItem;
}
foreach (ulong addr in Items[index].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
// foreach (ulong addr in Items[index].QuickAccessAddresses)
// {
// _quickAccess.Remove(addr);
// _fastQuickAccess.Remove(addr);
// }
Items[index] = rangeItem;
_quickAccess[item.Address] = rangeItem;
// _quickAccess[item.Address] = rangeItem;
return true;
}
@@ -108,18 +108,18 @@ namespace Ryujinx.Memory.Range
Items[index + 1].Previous = rangeItem;
}
foreach (ulong addr in item.QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
// foreach (ulong addr in item.QuickAccessAddresses)
// {
// _quickAccess.Remove(addr);
// _fastQuickAccess.Remove(addr);
// }
Items[index] = rangeItem;
if (item.Address != rangeItem.Address)
_quickAccess.Remove(item.Address);
_quickAccess[rangeItem.Address] = rangeItem;
// if (item.Address != rangeItem.Address)
// _quickAccess.Remove(item.Address);
//
// _quickAccess[rangeItem.Address] = rangeItem;
return true;
}
@@ -196,13 +196,13 @@ namespace Ryujinx.Memory.Range
if (index >= 0 && Items[index].Value.Equals(item))
{
_quickAccess.Remove(item.Address);
foreach (ulong addr in Items[index].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
// _quickAccess.Remove(item.Address);
//
// foreach (ulong addr in Items[index].QuickAccessAddresses)
// {
// _quickAccess.Remove(addr);
// _fastQuickAccess.Remove(addr);
// }
RemoveAt(index);
@@ -232,15 +232,15 @@ namespace Ryujinx.Memory.Range
(int startIndex, int endIndex) = BinarySearchEdges(startItem.Address, endItem.EndAddress);
for (int i = startIndex; i < endIndex; i++)
{
_quickAccess.Remove(Items[i].Address);
foreach (ulong addr in Items[i].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
}
// for (int i = startIndex; i < endIndex; i++)
// {
// _quickAccess.Remove(Items[i].Address);
// foreach (ulong addr in Items[i].QuickAccessAddresses)
// {
// _quickAccess.Remove(addr);
// _fastQuickAccess.Remove(addr);
// }
// }
if (endIndex < Count)
{
@@ -279,12 +279,12 @@ namespace Ryujinx.Memory.Range
while (Items[endIndex] is not null && Items[endIndex].Address < address + size)
{
_quickAccess.Remove(Items[endIndex].Address);
foreach (ulong addr in Items[endIndex].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
// _quickAccess.Remove(Items[endIndex].Address);
// foreach (ulong addr in Items[endIndex].QuickAccessAddresses)
// {
// _quickAccess.Remove(addr);
// _fastQuickAccess.Remove(addr);
// }
if (endIndex == Count - 1)
{
@@ -321,9 +321,9 @@ namespace Ryujinx.Memory.Range
{
Lock.EnterWriteLock();
Count = 0;
_quickAccess.Clear();
_fastQuickAccess.Clear();
Lock.ExitWriteLock();
// _quickAccess.Clear();
// _fastQuickAccess.Clear();
}
/// <summary>
@@ -344,7 +344,7 @@ namespace Ryujinx.Memory.Range
// So we need to return both the split 0-1 and 1-2 ranges.
Lock.EnterWriteLock();
(RangeItem<T> first, RangeItem<T> last) = FindOverlaps(address, size);
(RangeItem<T> first, RangeItem<T> last) = FindOverlapsAsNodes(address, size);
list = new List<T>();
if (first is null)
@@ -436,10 +436,10 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override RangeItem<T> FindOverlap(ulong address, ulong size)
{
if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap))
{
return overlap;
}
// if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap))
// {
// return overlap;
// }
int index = BinarySearchLeftEdge(address, address + size);
@@ -448,11 +448,11 @@ namespace Ryujinx.Memory.Range
return null;
}
if (Items[index].Address < address)
{
_quickAccess.TryAdd(address, Items[index]);
Items[index].QuickAccessAddresses.Add(address);
}
// if (Items[index].Address < address)
// {
// _quickAccess.TryAdd(address, Items[index]);
// Items[index].QuickAccessAddresses.Add(address);
// }
return Items[index];
}
@@ -466,10 +466,10 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
{
if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap) || _fastQuickAccess.TryGetValue(address, out overlap))
{
return overlap;
}
// if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap) || _fastQuickAccess.TryGetValue(address, out overlap))
// {
// return overlap;
// }
int index = BinarySearch(address, address + size);
@@ -478,16 +478,16 @@ namespace Ryujinx.Memory.Range
return null;
}
if (Items[index].Address < address)
{
_quickAccess.TryAdd(address, Items[index]);
}
else
{
_fastQuickAccess.TryAdd(address, Items[index]);
}
Items[index].QuickAccessAddresses.Add(address);
// if (Items[index].Address < address)
// {
// _quickAccess.TryAdd(address, Items[index]);
// }
// else
// {
// _fastQuickAccess.TryAdd(address, Items[index]);
// }
//
// Items[index].QuickAccessAddresses.Add(address);
return Items[index];
}
@@ -499,18 +499,8 @@ namespace Ryujinx.Memory.Range
/// <param name="size">Size in bytes of the range</param>
/// <returns>The first and last overlapping items, or null if none are found</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public (RangeItem<T>, RangeItem<T>) FindOverlaps(ulong address, ulong size)
public (RangeItem<T>, RangeItem<T>) FindOverlapsAsNodes(ulong address, ulong size)
{
if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap))
{
if (overlap.Next is null || overlap.Next.Address >= address + size)
{
return (overlap, overlap);
}
return (overlap, Items[BinarySearchRightEdge(address, address + size)]);
}
(int index, int endIndex) = BinarySearchEdges(address, address + size);
if (index < 0)
@@ -518,13 +508,45 @@ namespace Ryujinx.Memory.Range
return (null, null);
}
if (Items[index].Address < address)
return (Items[index], Items[endIndex - 1]);
}
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size)
{
(int index, int endIndex) = BinarySearchEdges(address, address + size);
RangeItem<T>[] result;
if (index < 0)
{
_quickAccess.TryAdd(address, Items[index]);
Items[index].QuickAccessAddresses.Add(address);
result = [];
}
else
{
result = new RangeItem<T>[endIndex - index];
Array.Copy(Items, index, result, 0, endIndex - index);
}
return (Items[index], Items[endIndex - 1]);
return result;
}
public Span<RangeItem<T>> FindOverlapsAsSpan(ulong address, ulong size)
{
(int index, int endIndex) = BinarySearchEdges(address, address + size);
Span<RangeItem<T>> result;
if (index < 0)
{
result = [];
}
else
{
result = Items.AsSpan().Slice(index, endIndex - index);
}
return result;
}
public override IEnumerator<T> GetEnumerator()
+10 -24
View File
@@ -1,4 +1,5 @@
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
namespace Ryujinx.Memory.Tracking
@@ -79,12 +80,10 @@ namespace Ryujinx.Memory.Tracking
{
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
regions.Lock.EnterReadLock();
(RangeItem<VirtualRegion> first, RangeItem<VirtualRegion> last) = regions.FindOverlaps(va, size);
RangeItem<VirtualRegion> current = first;
while (last != null && current != last.Next)
Span<RangeItem<VirtualRegion>> overlaps = regions.FindOverlapsAsSpan(va, size);
for (int i = 0; i < overlaps.Length; i++)
{
VirtualRegion region = current.Value;
VirtualRegion region = overlaps[i].Value;
// If the region has been fully remapped, signal that it has been mapped again.
bool remapped = _memoryManager.IsRangeMapped(region.Address, region.Size);
@@ -94,7 +93,6 @@ namespace Ryujinx.Memory.Tracking
}
region.UpdateProtection();
current = current.Next;
}
regions.Lock.ExitReadLock();
}
@@ -118,15 +116,11 @@ namespace Ryujinx.Memory.Tracking
{
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
regions.Lock.EnterReadLock();
(RangeItem<VirtualRegion> first, RangeItem<VirtualRegion> last) = regions.FindOverlaps(va, size);
Span<RangeItem<VirtualRegion>> overlaps = regions.FindOverlapsAsSpan(va, size);
RangeItem<VirtualRegion> current = first;
while (last != null && current != last.Next)
for (int i = 0; i < overlaps.Length; i++)
{
VirtualRegion region = current.Value;
region.SignalMappingChanged(false);
current = current.Next;
overlaps[i].Value.SignalMappingChanged(false);
}
regions.Lock.ExitReadLock();
}
@@ -303,21 +297,13 @@ namespace Ryujinx.Memory.Tracking
lock (TrackingLock)
{
NonOverlappingRangeList<VirtualRegion> regions = guest ? _guestVirtualRegions : _virtualRegions;
List<RangeItem<VirtualRegion>> overlaps = [];
// We use the non-span method here because keeping the lock will cause a deadlock.
regions.Lock.EnterReadLock();
(RangeItem<VirtualRegion> first, RangeItem<VirtualRegion> last) = regions.FindOverlaps(address, size);
RangeItem<VirtualRegion> current = first;
while (last != null && current != last.Next)
{
overlaps.Add(current);
current = current.Next;
}
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size);
regions.Lock.ExitReadLock();
if (first is null && !precise)
if (overlaps.Length == 0 && !precise)
{
if (_memoryManager.IsRangeMapped(address, size))
{
@@ -338,7 +324,7 @@ namespace Ryujinx.Memory.Tracking
size += (ulong)_pageSize;
}
for (int i = 0; i < overlaps.Count; i++)
for (int i = 0; i < overlaps.Length; i++)
{
VirtualRegion region = overlaps[i].Value;