From 10ee46744bb636d141def59506160ccea37e0077 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 11 Jan 2024 01:33:13 +0000 Subject: [PATCH] avoid allocations when doing address space lookup --- .../Collections/IntrusiveRedBlackTreeNode.cs | 8 +-- .../Jit/AddressIntrusiveRedBlackTree.cs | 35 +++++++++++ .../Jit/AddressSpacePageProtections.cs | 45 ++++++++++++-- src/Ryujinx.Cpu/Jit/AddressSpacePartition.cs | 62 ++++++++++++++----- .../Jit/AddressSpacePartitionAllocator.cs | 24 +++++-- 5 files changed, 145 insertions(+), 29 deletions(-) create mode 100644 src/Ryujinx.Cpu/Jit/AddressIntrusiveRedBlackTree.cs diff --git a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs index 8480d51ad..29d2d0c9a 100644 --- a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs +++ b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTreeNode.cs @@ -5,10 +5,10 @@ namespace Ryujinx.Common.Collections /// public class IntrusiveRedBlackTreeNode where T : IntrusiveRedBlackTreeNode { - internal bool Color = true; - internal T Left; - internal T Right; - internal T Parent; + public bool Color = true; + public T Left; + public T Right; + public T Parent; public T Predecessor => IntrusiveRedBlackTreeImpl.PredecessorOf((T)this); public T Successor => IntrusiveRedBlackTreeImpl.SuccessorOf((T)this); diff --git a/src/Ryujinx.Cpu/Jit/AddressIntrusiveRedBlackTree.cs b/src/Ryujinx.Cpu/Jit/AddressIntrusiveRedBlackTree.cs new file mode 100644 index 000000000..3084d16d1 --- /dev/null +++ b/src/Ryujinx.Cpu/Jit/AddressIntrusiveRedBlackTree.cs @@ -0,0 +1,35 @@ +using Ryujinx.Common.Collections; +using System; + +namespace Ryujinx.Cpu.Jit +{ + internal class AddressIntrusiveRedBlackTree : IntrusiveRedBlackTree where T : IntrusiveRedBlackTreeNode, IComparable, IComparable + { + /// + /// Retrieve the node that is considered equal to the specified address by the comparator. + /// + /// Address to compare with + /// Node that is equal to + public T GetNode(ulong address) + { + T node = Root; + while (node != null) + { + int cmp = node.CompareTo(address); + if (cmp < 0) + { + node = node.Left; + } + else if (cmp > 0) + { + node = node.Right; + } + else + { + return node; + } + } + return null; + } + } +} diff --git a/src/Ryujinx.Cpu/Jit/AddressSpacePageProtections.cs b/src/Ryujinx.Cpu/Jit/AddressSpacePageProtections.cs index 3f4cd2ff4..6d2e1f600 100644 --- a/src/Ryujinx.Cpu/Jit/AddressSpacePageProtections.cs +++ b/src/Ryujinx.Cpu/Jit/AddressSpacePageProtections.cs @@ -10,12 +10,15 @@ namespace Ryujinx.Cpu.Jit { private const ulong GuestPageSize = 0x1000; - class PageProtection : IntrusiveRedBlackTreeNode, IComparable + [ThreadStatic] + private static PageProtection _dummyProtection; + + class PageProtection : IntrusiveRedBlackTreeNode, IComparable, IComparable { public readonly AddressSpacePartitionAllocation Memory; public readonly ulong Offset; - public readonly ulong Address; - public readonly ulong Size; + public ulong Address; + public ulong Size; private MemoryBlock _viewBlock; @@ -63,9 +66,25 @@ namespace Ryujinx.Cpu.Jit return 1; } } + + public int CompareTo(ulong address) + { + if (address < Address) + { + return -1; + } + else if (address <= Address + Size - 1UL) + { + return 0; + } + else + { + return 1; + } + } } - private readonly IntrusiveRedBlackTree _protectionTree; + private readonly AddressIntrusiveRedBlackTree _protectionTree; public AddressSpacePageProtections() { @@ -99,7 +118,7 @@ namespace Ryujinx.Cpu.Jit { ulong pageSize = MemoryBlock.GetPageSize(); - PageProtection pageProtection = _protectionTree.GetNode(new PageProtection(default, 0, va, 1)); + PageProtection pageProtection = _protectionTree.GetNode(va); if (pageProtection == null) { @@ -310,7 +329,21 @@ namespace Ryujinx.Cpu.Jit private PageProtection GetLowestOverlap(ulong va, ulong size) { - PageProtection pageProtection = _protectionTree.GetNode(new PageProtection(default, 0, va, size)); + PageProtection lookup = _dummyProtection; + + if (lookup == null) + { + lookup = new(default, 0, va, size); + + _dummyProtection = lookup; + } + else + { + lookup.Address = va; + lookup.Size = size; + } + + PageProtection pageProtection = _protectionTree.GetNode(lookup); if (pageProtection == null) { diff --git a/src/Ryujinx.Cpu/Jit/AddressSpacePartition.cs b/src/Ryujinx.Cpu/Jit/AddressSpacePartition.cs index 7401a2784..3b983155d 100644 --- a/src/Ryujinx.Cpu/Jit/AddressSpacePartition.cs +++ b/src/Ryujinx.Cpu/Jit/AddressSpacePartition.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Cpu.Jit Private, } - private class Mapping : IntrusiveRedBlackTreeNode, IComparable + private class Mapping : IntrusiveRedBlackTreeNode, IComparable, IComparable { public ulong Address { get; private set; } public ulong Size { get; private set; } @@ -87,9 +87,25 @@ namespace Ryujinx.Cpu.Jit return 1; } } + + public int CompareTo(ulong address) + { + if (address < Address) + { + return -1; + } + else if (address <= EndAddress - 1UL) + { + return 0; + } + else + { + return 1; + } + } } - private class PrivateMapping : IntrusiveRedBlackTreeNode, IComparable + private class PrivateMapping : IntrusiveRedBlackTreeNode, IComparable, IComparable { public ulong Address { get; private set; } public ulong Size { get; private set; } @@ -158,13 +174,29 @@ namespace Ryujinx.Cpu.Jit return 1; } } + + public int CompareTo(ulong address) + { + if (address < Address) + { + return -1; + } + else if (address <= EndAddress - 1UL) + { + return 0; + } + else + { + return 1; + } + } } private readonly MemoryBlock _backingMemory; private readonly AddressSpacePartitionAllocation _baseMemory; private readonly PrivateMemoryAllocator _privateMemoryAllocator; - private readonly IntrusiveRedBlackTree _mappingTree; - private readonly IntrusiveRedBlackTree _privateTree; + private readonly AddressIntrusiveRedBlackTree _mappingTree; + private readonly AddressIntrusiveRedBlackTree _privateTree; private readonly AddressSpacePageProtections _pageProtections; private readonly ReaderWriterLockSlim _treeLock; @@ -185,8 +217,8 @@ namespace Ryujinx.Cpu.Jit public AddressSpacePartition(AddressSpacePartitionAllocation baseMemory, MemoryBlock backingMemory, ulong address, ulong size) { _privateMemoryAllocator = new PrivateMemoryAllocator(DefaultBlockAlignment, MemoryAllocationFlags.Mirrorable); - _mappingTree = new IntrusiveRedBlackTree(); - _privateTree = new IntrusiveRedBlackTree(); + _mappingTree = new AddressIntrusiveRedBlackTree(); + _privateTree = new AddressIntrusiveRedBlackTree(); _pageProtections = new AddressSpacePageProtections(); _treeLock = new ReaderWriterLockSlim(); @@ -212,7 +244,7 @@ namespace Ryujinx.Cpu.Jit try { - Mapping map = _mappingTree.GetNode(new Mapping(Address, Size, MappingType.None)); + Mapping map = _mappingTree.GetNode(Address); return map != null && map.Address == Address && map.Size == Size && map.Type == MappingType.None; } @@ -375,7 +407,7 @@ namespace Ryujinx.Cpu.Jit try { - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(Address, 1UL, default)); + PrivateMapping map = _privateTree.GetNode(Address); if (map != null && map.PrivateAllocation.IsValid) { @@ -398,7 +430,7 @@ namespace Ryujinx.Cpu.Jit { ulong pageAddress = EndAddress - _hostPageSize; - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(pageAddress, 1UL, default)); + PrivateMapping map = _privateTree.GetNode(pageAddress); if (map != null && map.PrivateAllocation.IsValid) { @@ -419,7 +451,7 @@ namespace Ryujinx.Cpu.Jit try { - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default)); + PrivateMapping map = _privateTree.GetNode(va); if (map != null && map.PrivateAllocation.IsValid) { @@ -440,7 +472,7 @@ namespace Ryujinx.Cpu.Jit try { - Mapping map = _mappingTree.GetNode(new Mapping(va, 1UL, MappingType.None)); + Mapping map = _mappingTree.GetNode(va); Update(map, va, pa, size, type); } @@ -534,7 +566,7 @@ namespace Ryujinx.Cpu.Jit ulong vaAligned = BitUtils.AlignDown(va, alignment); ulong endAddressAligned = BitUtils.AlignUp(endAddress, alignment); - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default)); + PrivateMapping map = _privateTree.GetNode(va); for (; map != null; map = map.Successor) { @@ -578,7 +610,7 @@ namespace Ryujinx.Cpu.Jit return; } - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(vaAligned, 1UL, default)); + PrivateMapping map = _privateTree.GetNode(vaAligned); for (; map != null; map = map.Successor) { @@ -639,7 +671,7 @@ namespace Ryujinx.Cpu.Jit try { - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default)); + PrivateMapping map = _privateTree.GetNode(va); nextVa = map.EndAddress; @@ -667,7 +699,7 @@ namespace Ryujinx.Cpu.Jit try { - PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, size, default)); + PrivateMapping map = _privateTree.GetNode(va); if (map != null && map.PrivateAllocation.IsValid) { diff --git a/src/Ryujinx.Cpu/Jit/AddressSpacePartitionAllocator.cs b/src/Ryujinx.Cpu/Jit/AddressSpacePartitionAllocator.cs index e897d05da..244639457 100644 --- a/src/Ryujinx.Cpu/Jit/AddressSpacePartitionAllocator.cs +++ b/src/Ryujinx.Cpu/Jit/AddressSpacePartitionAllocator.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Cpu.Jit private readonly MemoryTracking _tracking; private readonly MemoryEhMeilleure _memoryEh; - private class Mapping : IntrusiveRedBlackTreeNode, IComparable + private class Mapping : IntrusiveRedBlackTreeNode, IComparable, IComparable { public ulong Address { get; } public ulong Size { get; } @@ -94,9 +94,25 @@ namespace Ryujinx.Cpu.Jit return 1; } } + + public int CompareTo(ulong address) + { + if (address < Address) + { + return -1; + } + else if (address <= EndAddress - 1UL) + { + return 0; + } + else + { + return 1; + } + } } - private readonly IntrusiveRedBlackTree _mappingTree; + private readonly AddressIntrusiveRedBlackTree _mappingTree; private readonly object _lock; public Block(MemoryTracking tracking, MemoryBlock memory, ulong size, object locker) : base(memory, size) @@ -114,7 +130,7 @@ namespace Ryujinx.Cpu.Jit public void RemoveMapping(ulong offset, ulong size) { - _mappingTree.Remove(_mappingTree.GetNode(new Mapping(offset, size, 0, 0, 0))); + _mappingTree.Remove(_mappingTree.GetNode(offset)); } private bool VirtualMemoryEvent(ulong address, ulong size, bool write) @@ -123,7 +139,7 @@ namespace Ryujinx.Cpu.Jit lock (_lock) { - map = _mappingTree.GetNode(new Mapping(address, size, 0, 0, 0)); + map = _mappingTree.GetNode(address); } if (map == null)