From 666e05f5cb9aa9dc86874823c108586e7a5b38bd Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sun, 23 Apr 2023 22:06:23 -0400 Subject: Reducing Memory Allocations 202303 (#4624) * use ArrayPool, avoid 6000-7000 allocs/sec of runtime * use ArrayPool, avoid ~7k allocs/second during game execution * use ArrayPool, avoid ~3000 allocs/sec during game execution * use MemoryPool, reduce 0.5 MB/sec of new allocations during game execution * avoid over-allocation by setting List<> Capacity when known * remove LINQ in KTimeManager.UnscheduleFutureInvocation * KTimeManager - avoid spinning one more time when the time has arrived * KTimeManager - let SpinWait decide when to Thread.Yield(), and don't SpinOnce() immediately after Thread.Yield() * use MemoryPool, reduce ~175k bytes/sec allocation during game execution * IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array * Make ButtonMappingEntry a record struct to avoid allocations. Set the List capacity according to use. * add MemoryBuffer type for working with MemoryPool * update changes to use MemoryBuffer * make parameter ReadOnlySpan instead of Span * whitespace fix * Revert "IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array" This reverts commit f2c698bdf65f049e8481c9f2ec7138d9b9a8261d. * tweak KTimeManager spin behavior * replace MemoryBuffer with ByteMemoryPool modeled after System.Buffers.ArrayMemoryPool * make ByteMemoryPoolBuffer responsible for renting memory --- Ryujinx.Common/Memory/ByteMemoryPool.cs | 108 ++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Ryujinx.Common/Memory/ByteMemoryPool.cs (limited to 'Ryujinx.Common/Memory/ByteMemoryPool.cs') diff --git a/Ryujinx.Common/Memory/ByteMemoryPool.cs b/Ryujinx.Common/Memory/ByteMemoryPool.cs new file mode 100644 index 00000000..2910f408 --- /dev/null +++ b/Ryujinx.Common/Memory/ByteMemoryPool.cs @@ -0,0 +1,108 @@ +using System; +using System.Buffers; + +namespace Ryujinx.Common.Memory +{ + /// + /// Provides a pool of re-usable byte array instances. + /// + public sealed partial class ByteMemoryPool + { + private static readonly ByteMemoryPool _shared = new ByteMemoryPool(); + + /// + /// Constructs a instance. Private to force access through + /// the instance. + /// + private ByteMemoryPool() + { + // No implementation + } + + /// + /// Retrieves a shared instance. + /// + public static ByteMemoryPool Shared => _shared; + + /// + /// Returns the maximum buffer size supported by this pool. + /// + public int MaxBufferSize => Array.MaxLength; + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner Rent(long length) + => RentImpl(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner Rent(ulong length) + => RentImpl(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner Rent(int length) + => RentImpl(length); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner RentCleared(long length) + => RentCleared(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner RentCleared(ulong length) + => RentCleared(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner RentCleared(int length) + { + var buffer = RentImpl(length); + + buffer.Memory.Span.Clear(); + + return buffer; + } + + private static ByteMemoryPoolBuffer RentImpl(int length) + { + if ((uint)length > Array.MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(length), length, null); + } + + return new ByteMemoryPoolBuffer(length); + } + } +} -- cgit v1.2.3