From ce438f7276b7d30c1d20ecc8fcd0df0f6dccdb77 Mon Sep 17 00:00:00 2001 From: LotP1 <68976644+LotP1@users.noreply.github.com> Date: Mon, 2 Jun 2025 01:17:34 +0200 Subject: [PATCH] implement quickAccess for fast look-up some values are reused often and will be quicker to access by first checking an array of the last used items. removed and renamed some older functions. --- .../Memory/BufferCache.cs | 12 +- src/Ryujinx.Memory/Range/RangeList.cs | 257 ++++++------------ 2 files changed, 85 insertions(+), 184 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 41d2af024..6c2520979 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -335,7 +335,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask; ulong alignedSize = alignedEndAddress - alignedAddress; - Buffer buffer = _buffers.FindFirstOverlap(alignedAddress, alignedSize); + Buffer buffer = _buffers.FindOverlapFast(alignedAddress, alignedSize); BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false); alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize); @@ -402,7 +402,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (subRange.Address != MemoryManager.PteUnmapped) { - Buffer buffer = _buffers.FindFirstOverlap(subRange.Address, subRange.Size); + Buffer buffer = _buffers.FindOverlapFast(subRange.Address, subRange.Size); virtualBuffer.AddPhysicalDependency(buffer, subRange.Address, dstOffset, subRange.Size); physicalBuffers.Add(buffer); @@ -890,7 +890,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { MemoryRange subRange = range.GetSubRange(i); - Buffer subBuffer = _buffers.FindFirstOverlap(subRange.Address, subRange.Size); + Buffer subBuffer = _buffers.FindOverlapFast(subRange.Address, subRange.Size); subBuffer.SynchronizeMemory(subRange.Address, subRange.Size); @@ -938,7 +938,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (size != 0) { - buffer = _buffers.FindFirstOverlap(address, size); + buffer = _buffers.FindOverlapFast(address, size); buffer.CopyFromDependantVirtualBuffers(); buffer.SynchronizeMemory(address, size); @@ -950,7 +950,7 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - buffer = _buffers.FindFirstOverlap(address, 1); + buffer = _buffers.FindOverlapFast(address, 1); } return buffer; @@ -988,7 +988,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (size != 0) { - Buffer buffer = _buffers.FindFirstOverlap(address, size); + Buffer buffer = _buffers.FindOverlapFast(address, size); if (copyBackVirtual) { diff --git a/src/Ryujinx.Memory/Range/RangeList.cs b/src/Ryujinx.Memory/Range/RangeList.cs index 980fe0ad8..c2e39b576 100644 --- a/src/Ryujinx.Memory/Range/RangeList.cs +++ b/src/Ryujinx.Memory/Range/RangeList.cs @@ -1,27 +1,17 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; -using System.Numerics; using System.Runtime.CompilerServices; using System.Threading; namespace Ryujinx.Memory.Range { - public readonly struct RangeItem where TValue : IRange + public readonly struct RangeItem(TValue value) where TValue : IRange { - public readonly ulong Address; - public readonly ulong EndAddress; + public readonly ulong Address = value.Address; + public readonly ulong EndAddress = value.Address + value.Size; - public readonly TValue Value; - - public RangeItem(TValue value) - { - Value = value; - - Address = value.Address; - EndAddress = value.Address + value.Size; - } + public readonly TValue Value = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool OverlapsWith(ulong address, ulong endAddress) @@ -29,6 +19,7 @@ namespace Ryujinx.Memory.Range return Address < endAddress && address < EndAddress; } } + /// /// Result of an Overlaps Finder function. /// @@ -62,7 +53,12 @@ namespace Ryujinx.Memory.Range public int Count { get; protected set; } - public readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); + public readonly ReaderWriterLockSlim Lock = new(); + + private const int QuickAccessLength = 8; + private int _offset; + private int _count; + private RangeItem[] _quickAccess = new RangeItem[QuickAccessLength]; /// /// Creates a new range list. @@ -95,7 +91,7 @@ namespace Ryujinx.Memory.Range /// /// The item to be updated /// True if the item was located and updated, false otherwise - public bool Update(T item) + protected bool Update(T item) { int index = BinarySearchLeftEdge(item.Address); @@ -106,6 +102,10 @@ namespace Ryujinx.Memory.Range if (_items[index].Value.Equals(item)) { _items[index] = new RangeItem(item); + + _quickAccess = new RangeItem[QuickAccessLength]; + _count = 0; + _offset = 0; return true; } @@ -147,27 +147,19 @@ namespace Ryujinx.Memory.Range } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveAt(int index) + protected void RemoveAt(int index) { if (index < --Count) { Array.Copy(_items, index + 1, _items, index, Count - index); } - } - public void RemoveRange(int start, int end) - { - if (end <= Count) - { - Array.Copy(_items, end, _items, start, Count - end); - Count -= end - start; - } - else if (end == Count) - { - Count = start; - } + _quickAccess = new RangeItem[QuickAccessLength]; + _count = 0; + _offset = 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveRange(OverlapResult overlapResult) { if (overlapResult.EndIndex < Count) @@ -179,6 +171,10 @@ namespace Ryujinx.Memory.Range { Count = overlapResult.StartIndex; } + + _quickAccess = new RangeItem[QuickAccessLength]; + _count = 0; + _offset = 0; } /// @@ -214,62 +210,39 @@ namespace Ryujinx.Memory.Range } /// - /// Updates an item's end address. - /// - /// The item to be updated - public void UpdateEndAddress(T item) - { - int index = BinarySearchLeftEdge(item.Address); - - if (index >= 0) - { - while (index < Count) - { - if (_items[index].Value.Equals(item)) - { - _items[index] = new RangeItem(item); - - return; - } - - if (_items[index].Address > item.Address) - { - break; - } - - index++; - } - } - } - - /// - /// Gets the first item on the list overlapping in memory with the specified item. + /// Gets an item on the list overlapping the specified memory range. /// /// - /// Despite the name, this has no ordering guarantees of the returned item. - /// It only ensures that the item returned overlaps the specified item. - /// - /// Item to check for overlaps - /// The overlapping item, or the default value for the type if none found - public T FindFirstOverlap(T item) - { - return FindFirstOverlap(item.Address, item.Size); - } - - /// - /// Gets the first item on the list overlapping the specified memory range. - /// - /// - /// Despite the name, this has no ordering guarantees of the returned item. + /// This has no ordering guarantees of the returned item. /// It only ensures that the item returned overlaps the specified memory range. /// /// Start address of the range /// Size in bytes of the range /// The overlapping item, or the default value for the type if none found - public T FindFirstOverlap(ulong address, ulong size) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T FindOverlapFast(ulong address, ulong size) { + for (int i = 0; i < _quickAccess.Length; i++) + { + ref RangeItem item = ref _quickAccess[(i + _offset) % _quickAccess.Length]; + + if (item.OverlapsWith(address, address + size)) + { + return item.Value; + } + } + int index = BinarySearch(address, address + size); + if (_count < _quickAccess.Length) + { + _quickAccess[_count++] = _items[index]; + } + else + { + _quickAccess[_offset++ % _quickAccess.Length] = _items[index]; + } + if (index < 0) { return default; @@ -277,18 +250,7 @@ namespace Ryujinx.Memory.Range return _items[index].Value; } - - /// - /// Gets all items overlapping with the specified item in memory. - /// - /// Item to check for overlaps - /// Output array where matches will be written. It is automatically resized to fit the results - /// The number of overlapping items found - public OverlapResult FindOverlaps(T item, ref RangeItem[] output) - { - return FindOverlaps(item.Address, item.Size, ref output); - } - + /// /// Gets all items on the list overlapping the specified memory range. /// @@ -301,10 +263,13 @@ namespace Ryujinx.Memory.Range int outputIndex = 0; ulong endAddress = address + size; - + + int startIndex = BinarySearchLeftEdge(address); + if (startIndex < 0) + startIndex = ~startIndex; int endIndex = -1; - for (int i = 0; i < Count; i++) + for (int i = startIndex; i < Count; i++) { ref RangeItem item = ref _items[i]; @@ -325,29 +290,26 @@ namespace Ryujinx.Memory.Range endIndex = Count; } - if (endIndex - outputIndex >= 0) + if (outputIndex > 0 && outputIndex == endIndex - startIndex) { Array.Resize(ref output, outputIndex); Array.Copy(_items, endIndex - outputIndex, output, 0, outputIndex); + + return new OverlapResult(startIndex, endIndex); } - - return new OverlapResult(endIndex - outputIndex, endIndex); - } - - /// - /// Gets all items overlapping with the specified item in memory. - /// - /// - /// This method only returns correct results if none of the items on the list overlaps with - /// each other. If that is not the case, this method should not be used. - /// This method is faster than the regular method to find all overlaps. - /// - /// Item to check for overlaps - /// Output array where matches will be written. It is automatically resized to fit the results - /// The number of overlapping items found - public OverlapResult FindOverlapsNonOverlapping(T item, ref RangeItem[] output) - { - return FindOverlapsNonOverlapping(item.Address, item.Size, ref output); + else if (outputIndex > 0) + { + Array.Resize(ref output, outputIndex); + int arrIndex = 0; + for (int i = startIndex; i < endIndex; i++) + { + output[arrIndex++] = _items[i]; + } + + return new OverlapResult(endIndex - outputIndex, endIndex); + } + + return new OverlapResult(); } /// @@ -362,6 +324,7 @@ namespace Ryujinx.Memory.Range /// Size in bytes of the range /// Output array where matches will be written. It is automatically resized to fit the results /// The number of overlapping items found + [MethodImpl(MethodImplOptions.AggressiveInlining)] public OverlapResult FindOverlapsNonOverlapping(ulong address, ulong size, ref RangeItem[] output) { // This is a bit faster than FindOverlaps, but only works @@ -380,6 +343,7 @@ namespace Ryujinx.Memory.Range return new OverlapResult(index, endIndex); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public OverlapResult FindOverlapsNonOverlappingAsSpan(ulong address, ulong size, out ReadOnlySpan> span) { // This is a bit faster than FindOverlaps, but only works @@ -410,6 +374,7 @@ namespace Ryujinx.Memory.Range /// Start address of the range /// Size in bytes of the range /// Range information of overlapping items found + [MethodImpl(MethodImplOptions.AggressiveInlining)] public OverlapResult FindOverlapsNonOverlapping(ulong address, ulong size) { // This is a bit faster than FindOverlaps, but only works @@ -428,6 +393,7 @@ namespace Ryujinx.Memory.Range /// Address to find /// Output array where matches will be written. It is automatically resized to fit the results /// The number of matches found + [MethodImpl(MethodImplOptions.AggressiveInlining)] public OverlapResult FindOverlaps(ulong address, ref RangeItem[] output) { (int index, int endIndex) = BinarySearchEdges(address); @@ -446,6 +412,7 @@ namespace Ryujinx.Memory.Range /// /// Address to find /// List index of the item, or complement index of nearest item with lower value on the list + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int BinarySearch(ulong address) { int left = 0; @@ -482,6 +449,7 @@ namespace Ryujinx.Memory.Range /// /// Address to find /// List index of the left-most item that overlaps, or complement index of nearest item with lower value on the list + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int BinarySearchLeftEdge(ulong address) { if (Count == 0) @@ -550,6 +518,7 @@ namespace Ryujinx.Memory.Range /// /// Start address of the range /// List index of the left-most item that overlaps, or complement index of nearest item with lower value on the list + [MethodImpl(MethodImplOptions.AggressiveInlining)] private (int, int) BinarySearchEdges(ulong address) { if (Count == 0) @@ -679,6 +648,7 @@ namespace Ryujinx.Memory.Range /// Start address of the range /// End address of the range /// List index of the item, or complement index of nearest item with lower value on the list + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int BinarySearch(ulong address, ulong endAddress) { int left = 0; @@ -716,76 +686,7 @@ namespace Ryujinx.Memory.Range /// Start address of the range /// End address of the range /// List index of the left-most item that overlaps, or complement index of nearest item with lower value on the list - private int BinarySearchLeftEdge(ulong address, ulong endAddress) - { - if (Count == 0) - return ~0; - - if (Count == 1) - { - ref RangeItem item = ref _items[0]; - - if (item.OverlapsWith(address, endAddress)) - { - return 0; - } - - if (address < item.Address) - { - return ~0; - } - else - { - return ~1; - } - } - - int left = 0; - int right = Count - 1; - - while (left <= right) - { - int range = right - left; - - int middle = left + (range >> 1); - - ref RangeItem item = ref _items[middle]; - - bool match = item.OverlapsWith(address, endAddress); - - if (range == 0) - { - if (match) - return middle; - else if (address < item.Address) - return ~(right); - else - return ~(right + 1); - } - - if (match) - { - right = middle; - } - else if(address < item.Address) - { - right = middle - 1; - } - else - { - left = middle + 1; - } - } - - return ~left; - } - - /// - /// Performs binary search for items overlapping a given memory range. - /// - /// Start address of the range - /// End address of the range - /// List index of the left-most item that overlaps, or complement index of nearest item with lower value on the list + [MethodImpl(MethodImplOptions.AggressiveInlining)] private (int, int) BinarySearchEdges(ulong address, ulong endAddress) { if (Count == 0)